diff --git a/pom.xml b/pom.xml index cdad8d7..4ebc3a6 100644 --- a/pom.xml +++ b/pom.xml @@ -23,8 +23,8 @@ - yudao-module-erp - + + yudao-module-iot diff --git a/yudao-dependencies/pom.xml b/yudao-dependencies/pom.xml index 6885fe6..87f7a68 100644 --- a/yudao-dependencies/pom.xml +++ b/yudao-dependencies/pom.xml @@ -25,7 +25,7 @@ 4.5.0 2.5 - 1.2.27 + 1.2.23 3.5.19 3.5.15 1.5.5 @@ -53,6 +53,7 @@ 1.4.0 1.21.2 + 4.0.3 1.18.42 1.6.3 5.8.42 @@ -679,6 +680,14 @@ ${weixin-java.version} + + + com.alibaba + easyexcel + ${easyexcel.verion} + + + org.jeecgframework.jimureport diff --git a/yudao-framework/yudao-common/src/main/java/cn/iocoder/yudao/framework/common/core/IntArrayValuable.java b/yudao-framework/yudao-common/src/main/java/cn/iocoder/yudao/framework/common/core/IntArrayValuable.java new file mode 100644 index 0000000..8914231 --- /dev/null +++ b/yudao-framework/yudao-common/src/main/java/cn/iocoder/yudao/framework/common/core/IntArrayValuable.java @@ -0,0 +1,15 @@ +package cn.iocoder.yudao.framework.common.core; + +/** + * 可生成 Int 数组的接口 + * + * @author 芋道源码 + */ +public interface IntArrayValuable { + + /** + * @return int 数组 + */ + int[] array(); + +} diff --git a/yudao-framework/yudao-common/src/main/java/cn/iocoder/yudao/framework/common/enums/DeviceConnectionStatusEnum.java b/yudao-framework/yudao-common/src/main/java/cn/iocoder/yudao/framework/common/enums/DeviceConnectionStatusEnum.java new file mode 100644 index 0000000..15c7a9f --- /dev/null +++ b/yudao-framework/yudao-common/src/main/java/cn/iocoder/yudao/framework/common/enums/DeviceConnectionStatusEnum.java @@ -0,0 +1,67 @@ +package cn.iocoder.yudao.framework.common.enums; + +import cn.hutool.core.util.ObjUtil; +import cn.iocoder.yudao.framework.common.core.IntArrayValuable; +import lombok.AllArgsConstructor; +import lombok.Getter; + +import java.util.Arrays; + +/** + * 设备连接状态枚举 + * + * @author 芋道源码 + */ +@Getter +@AllArgsConstructor +public enum DeviceConnectionStatusEnum implements IntArrayValuable { + + CONNECTED(1, "已连接"), + DISCONNECTED(2, "已断开"); + + public static final int[] ARRAYS = Arrays.stream(values()).mapToInt(DeviceConnectionStatusEnum::getStatus).toArray(); + + /** + * 状态值 + */ + private final Integer status; + + /** + * 状态名 + */ + private final String name; + + @Override + public int[] array() { + return ARRAYS; + } + + /** + * 判断是否为连接状态 + */ + public static boolean isConnected(Integer status) { + return ObjUtil.equal(CONNECTED.status, status); + } + + /** + * 判断是否为断开状态 + */ + public static boolean isDisconnected(Integer status) { + return ObjUtil.equal(DISCONNECTED.status, status); + } + + /** + * 根据状态值获取枚举实例 + */ + public static DeviceConnectionStatusEnum valueOf(Integer status) { + if (status == null) { + return null; + } + for (DeviceConnectionStatusEnum value : values()) { + if (ObjUtil.equal(value.status, status)) { + return value; + } + } + return null; + } +} \ No newline at end of file diff --git a/yudao-framework/yudao-common/src/main/java/cn/iocoder/yudao/framework/common/pojo/DeviceEdgeData.java b/yudao-framework/yudao-common/src/main/java/cn/iocoder/yudao/framework/common/pojo/DeviceEdgeData.java new file mode 100644 index 0000000..fb7f8e0 --- /dev/null +++ b/yudao-framework/yudao-common/src/main/java/cn/iocoder/yudao/framework/common/pojo/DeviceEdgeData.java @@ -0,0 +1,14 @@ +package cn.iocoder.yudao.framework.common.pojo; + +import lombok.Data; + +import java.time.LocalDateTime; + +@Data +public class DeviceEdgeData { + private Long deviceId; + private LocalDateTime firstTs; + private String firstData; + private LocalDateTime lastTs; + private String lastData; +} diff --git a/yudao-framework/yudao-spring-boot-starter-excel/pom.xml b/yudao-framework/yudao-spring-boot-starter-excel/pom.xml index b121818..c6412a7 100644 --- a/yudao-framework/yudao-spring-boot-starter-excel/pom.xml +++ b/yudao-framework/yudao-spring-boot-starter-excel/pom.xml @@ -41,9 +41,13 @@ + + + + - cn.idev.excel - fastexcel + com.alibaba + easyexcel diff --git a/yudao-framework/yudao-spring-boot-starter-excel/src/main/java/cn/iocoder/yudao/framework/excel/core/convert/AreaConvert.java b/yudao-framework/yudao-spring-boot-starter-excel/src/main/java/cn/iocoder/yudao/framework/excel/core/convert/AreaConvert.java index b5ca863..9778b17 100644 --- a/yudao-framework/yudao-spring-boot-starter-excel/src/main/java/cn/iocoder/yudao/framework/excel/core/convert/AreaConvert.java +++ b/yudao-framework/yudao-spring-boot-starter-excel/src/main/java/cn/iocoder/yudao/framework/excel/core/convert/AreaConvert.java @@ -3,11 +3,11 @@ package cn.iocoder.yudao.framework.excel.core.convert; import cn.hutool.core.convert.Convert; import cn.iocoder.yudao.framework.ip.core.Area; import cn.iocoder.yudao.framework.ip.core.utils.AreaUtils; -import cn.idev.excel.converters.Converter; -import cn.idev.excel.enums.CellDataTypeEnum; -import cn.idev.excel.metadata.GlobalConfiguration; -import cn.idev.excel.metadata.data.ReadCellData; -import cn.idev.excel.metadata.property.ExcelContentProperty; +import com.alibaba.excel.converters.Converter; +import com.alibaba.excel.enums.CellDataTypeEnum; +import com.alibaba.excel.metadata.GlobalConfiguration; +import com.alibaba.excel.metadata.data.ReadCellData; +import com.alibaba.excel.metadata.property.ExcelContentProperty; import lombok.extern.slf4j.Slf4j; /** diff --git a/yudao-framework/yudao-spring-boot-starter-excel/src/main/java/cn/iocoder/yudao/framework/excel/core/convert/DictConvert.java b/yudao-framework/yudao-spring-boot-starter-excel/src/main/java/cn/iocoder/yudao/framework/excel/core/convert/DictConvert.java index b9e0dcb..39e1ae6 100644 --- a/yudao-framework/yudao-spring-boot-starter-excel/src/main/java/cn/iocoder/yudao/framework/excel/core/convert/DictConvert.java +++ b/yudao-framework/yudao-spring-boot-starter-excel/src/main/java/cn/iocoder/yudao/framework/excel/core/convert/DictConvert.java @@ -3,12 +3,20 @@ package cn.iocoder.yudao.framework.excel.core.convert; import cn.hutool.core.convert.Convert; import cn.iocoder.yudao.framework.dict.core.DictFrameworkUtils; import cn.iocoder.yudao.framework.excel.core.annotations.DictFormat; -import cn.idev.excel.converters.Converter; -import cn.idev.excel.enums.CellDataTypeEnum; -import cn.idev.excel.metadata.GlobalConfiguration; -import cn.idev.excel.metadata.data.ReadCellData; -import cn.idev.excel.metadata.data.WriteCellData; -import cn.idev.excel.metadata.property.ExcelContentProperty; +import cn.hutool.core.convert.Convert; +import cn.iocoder.yudao.framework.dict.core.DictFrameworkUtils; +import cn.iocoder.yudao.framework.excel.core.annotations.DictFormat; +import com.alibaba.excel.converters.Converter; +import com.alibaba.excel.enums.CellDataTypeEnum; +import com.alibaba.excel.metadata.GlobalConfiguration; +import com.alibaba.excel.metadata.data.ReadCellData; +import com.alibaba.excel.metadata.data.WriteCellData; +import com.alibaba.excel.metadata.property.ExcelContentProperty; +import lombok.extern.slf4j.Slf4j; + + + + import lombok.extern.slf4j.Slf4j; /** diff --git a/yudao-framework/yudao-spring-boot-starter-excel/src/main/java/cn/iocoder/yudao/framework/excel/core/convert/JsonConvert.java b/yudao-framework/yudao-spring-boot-starter-excel/src/main/java/cn/iocoder/yudao/framework/excel/core/convert/JsonConvert.java index 6958c32..f095b0d 100644 --- a/yudao-framework/yudao-spring-boot-starter-excel/src/main/java/cn/iocoder/yudao/framework/excel/core/convert/JsonConvert.java +++ b/yudao-framework/yudao-spring-boot-starter-excel/src/main/java/cn/iocoder/yudao/framework/excel/core/convert/JsonConvert.java @@ -1,12 +1,11 @@ package cn.iocoder.yudao.framework.excel.core.convert; import cn.iocoder.yudao.framework.common.util.json.JsonUtils; -import cn.idev.excel.converters.Converter; -import cn.idev.excel.enums.CellDataTypeEnum; -import cn.idev.excel.metadata.GlobalConfiguration; -import cn.idev.excel.metadata.data.WriteCellData; -import cn.idev.excel.metadata.property.ExcelContentProperty; - +import com.alibaba.excel.converters.Converter; +import com.alibaba.excel.enums.CellDataTypeEnum; +import com.alibaba.excel.metadata.GlobalConfiguration; +import com.alibaba.excel.metadata.data.WriteCellData; +import com.alibaba.excel.metadata.property.ExcelContentProperty; /** * Excel Json 转换器 * diff --git a/yudao-framework/yudao-spring-boot-starter-excel/src/main/java/cn/iocoder/yudao/framework/excel/core/convert/MoneyConvert.java b/yudao-framework/yudao-spring-boot-starter-excel/src/main/java/cn/iocoder/yudao/framework/excel/core/convert/MoneyConvert.java index 9ed0bd5..ee66fe7 100644 --- a/yudao-framework/yudao-spring-boot-starter-excel/src/main/java/cn/iocoder/yudao/framework/excel/core/convert/MoneyConvert.java +++ b/yudao-framework/yudao-spring-boot-starter-excel/src/main/java/cn/iocoder/yudao/framework/excel/core/convert/MoneyConvert.java @@ -1,10 +1,10 @@ package cn.iocoder.yudao.framework.excel.core.convert; -import cn.idev.excel.converters.Converter; -import cn.idev.excel.enums.CellDataTypeEnum; -import cn.idev.excel.metadata.GlobalConfiguration; -import cn.idev.excel.metadata.data.WriteCellData; -import cn.idev.excel.metadata.property.ExcelContentProperty; +import com.alibaba.excel.converters.Converter; +import com.alibaba.excel.enums.CellDataTypeEnum; +import com.alibaba.excel.metadata.GlobalConfiguration; +import com.alibaba.excel.metadata.data.WriteCellData; +import com.alibaba.excel.metadata.property.ExcelContentProperty; import java.math.BigDecimal; import java.math.RoundingMode; diff --git a/yudao-framework/yudao-spring-boot-starter-excel/src/main/java/cn/iocoder/yudao/framework/excel/core/handler/ColumnWidthMatchStyleStrategy.java b/yudao-framework/yudao-spring-boot-starter-excel/src/main/java/cn/iocoder/yudao/framework/excel/core/handler/ColumnWidthMatchStyleStrategy.java index 49a5b31..65be84a 100644 --- a/yudao-framework/yudao-spring-boot-starter-excel/src/main/java/cn/iocoder/yudao/framework/excel/core/handler/ColumnWidthMatchStyleStrategy.java +++ b/yudao-framework/yudao-spring-boot-starter-excel/src/main/java/cn/iocoder/yudao/framework/excel/core/handler/ColumnWidthMatchStyleStrategy.java @@ -1,13 +1,14 @@ package cn.iocoder.yudao.framework.excel.core.handler; import cn.hutool.core.collection.CollUtil; -import cn.idev.excel.enums.CellDataTypeEnum; -import cn.idev.excel.metadata.Head; -import cn.idev.excel.metadata.data.WriteCellData; -import cn.idev.excel.util.MapUtils; -import cn.idev.excel.write.metadata.holder.WriteSheetHolder; -import cn.idev.excel.write.style.column.AbstractColumnWidthStyleStrategy; -import cn.idev.excel.write.style.column.LongestMatchColumnWidthStyleStrategy; + +import com.alibaba.excel.*; +import com.alibaba.excel.enums.CellDataTypeEnum; +import com.alibaba.excel.metadata.Head; +import com.alibaba.excel.metadata.data.WriteCellData; +import com.alibaba.excel.util.MapUtils; +import com.alibaba.excel.write.metadata.holder.WriteSheetHolder; +import com.alibaba.excel.write.style.column.AbstractColumnWidthStyleStrategy; import org.apache.poi.ss.usermodel.Cell; import java.util.HashMap; diff --git a/yudao-framework/yudao-spring-boot-starter-excel/src/main/java/cn/iocoder/yudao/framework/excel/core/handler/SelectSheetWriteHandler.java b/yudao-framework/yudao-spring-boot-starter-excel/src/main/java/cn/iocoder/yudao/framework/excel/core/handler/SelectSheetWriteHandler.java index c55e121..ef1eb65 100644 --- a/yudao-framework/yudao-spring-boot-starter-excel/src/main/java/cn/iocoder/yudao/framework/excel/core/handler/SelectSheetWriteHandler.java +++ b/yudao-framework/yudao-spring-boot-starter-excel/src/main/java/cn/iocoder/yudao/framework/excel/core/handler/SelectSheetWriteHandler.java @@ -10,19 +10,16 @@ import cn.iocoder.yudao.framework.common.core.KeyValue; import cn.iocoder.yudao.framework.dict.core.DictFrameworkUtils; import cn.iocoder.yudao.framework.excel.core.annotations.ExcelColumnSelect; import cn.iocoder.yudao.framework.excel.core.function.ExcelColumnSelectFunction; -import cn.idev.excel.annotation.ExcelIgnore; -import cn.idev.excel.annotation.ExcelIgnoreUnannotated; -import cn.idev.excel.annotation.ExcelProperty; -import cn.idev.excel.write.handler.SheetWriteHandler; -import cn.idev.excel.write.metadata.holder.WriteSheetHolder; -import cn.idev.excel.write.metadata.holder.WriteWorkbookHolder; +import com.alibaba.excel.annotation.ExcelProperty; +import com.alibaba.excel.write.handler.SheetWriteHandler; +import com.alibaba.excel.write.metadata.holder.WriteSheetHolder; +import com.alibaba.excel.write.metadata.holder.WriteWorkbookHolder; import lombok.extern.slf4j.Slf4j; import org.apache.poi.hssf.usermodel.HSSFDataValidation; import org.apache.poi.ss.usermodel.*; import org.apache.poi.ss.util.CellRangeAddressList; import java.lang.reflect.Field; -import java.lang.reflect.Modifier; import java.util.Comparator; import java.util.HashMap; import java.util.List; @@ -59,20 +56,7 @@ public class SelectSheetWriteHandler implements SheetWriteHandler { public SelectSheetWriteHandler(Class head) { // 解析下拉数据 int colIndex = 0; - boolean ignoreUnannotated = head.isAnnotationPresent(ExcelIgnoreUnannotated.class); for (Field field : head.getDeclaredFields()) { - // 关联 https://github.com/YunaiV/ruoyi-vue-pro/pull/853 - // 1.1 忽略 static final 或 transient 的字段 - if (isStaticFinalOrTransient(field) ) { - continue; - } - // 1.2 忽略的字段跳过 - if ((ignoreUnannotated && !field.isAnnotationPresent(ExcelProperty.class)) - || field.isAnnotationPresent(ExcelIgnore.class)) { - continue; - } - - // 2. 核心:处理有 ExcelColumnSelect 注解的字段 if (field.isAnnotationPresent(ExcelColumnSelect.class)) { ExcelProperty excelProperty = field.getAnnotation(ExcelProperty.class); if (excelProperty != null && excelProperty.index() != -1) { @@ -84,19 +68,6 @@ public class SelectSheetWriteHandler implements SheetWriteHandler { } } - /** - * 判断字段是否是静态的、最终的、 transient 的 - * 原因:FastExcel 默认是忽略 static final 或 transient 的字段,所以需要判断 - * - * @param field 字段 - * @return 是否是静态的、最终的、transient 的 - */ - private boolean isStaticFinalOrTransient(Field field) { - return (Modifier.isStatic(field.getModifiers()) && Modifier.isFinal(field.getModifiers())) - || Modifier.isTransient(field.getModifiers()); - } - - /** * 获得下拉数据,并添加到 {@link #selectMap} 中 * @@ -184,4 +155,4 @@ public class SelectSheetWriteHandler implements SheetWriteHandler { writeSheetHolder.getSheet().addValidationData(validation); } -} +} \ No newline at end of file diff --git a/yudao-framework/yudao-spring-boot-starter-excel/src/main/java/cn/iocoder/yudao/framework/excel/core/util/ExcelUtils.java b/yudao-framework/yudao-spring-boot-starter-excel/src/main/java/cn/iocoder/yudao/framework/excel/core/util/ExcelUtils.java index 8e37452..0bc5994 100644 --- a/yudao-framework/yudao-spring-boot-starter-excel/src/main/java/cn/iocoder/yudao/framework/excel/core/util/ExcelUtils.java +++ b/yudao-framework/yudao-spring-boot-starter-excel/src/main/java/cn/iocoder/yudao/framework/excel/core/util/ExcelUtils.java @@ -1,15 +1,15 @@ package cn.iocoder.yudao.framework.excel.core.util; -import cn.idev.excel.FastExcelFactory; -import cn.idev.excel.converters.longconverter.LongStringConverter; -import cn.iocoder.yudao.framework.common.util.http.HttpUtils; -import cn.iocoder.yudao.framework.excel.core.handler.ColumnWidthMatchStyleStrategy; import cn.iocoder.yudao.framework.excel.core.handler.SelectSheetWriteHandler; +import com.alibaba.excel.EasyExcel; +import com.alibaba.excel.converters.longconverter.LongStringConverter; +import com.alibaba.excel.write.style.column.LongestMatchColumnWidthStyleStrategy; import org.springframework.web.multipart.MultipartFile; import javax.servlet.http.HttpServletResponse; import java.io.IOException; -import java.io.InputStream; +import java.net.URLEncoder; +import java.nio.charset.StandardCharsets; import java.util.List; /** @@ -33,24 +33,21 @@ public class ExcelUtils { public static void write(HttpServletResponse response, String filename, String sheetName, Class head, List data) throws IOException { // 输出 Excel - FastExcelFactory.write(response.getOutputStream(), head) + EasyExcel.write(response.getOutputStream(), head) .autoCloseStream(false) // 不要自动关闭,交给 Servlet 自己处理 - .registerWriteHandler(new ColumnWidthMatchStyleStrategy()) // 基于 column 长度,自动适配。最大 255 宽度 + .registerWriteHandler(new LongestMatchColumnWidthStyleStrategy()) // 基于 column 长度,自动适配。最大 255 宽度 .registerWriteHandler(new SelectSheetWriteHandler(head)) // 基于固定 sheet 实现下拉框 .registerConverter(new LongStringConverter()) // 避免 Long 类型丢失精度 .sheet(sheetName).doWrite(data); // 设置 header 和 contentType。写在最后的原因是,避免报错时,响应 contentType 已经被修改了 - response.addHeader("Content-Disposition", "attachment;filename=" + HttpUtils.encodeUtf8(filename)); + response.addHeader("Content-Disposition", "attachment;filename=" + URLEncoder.encode(filename, StandardCharsets.UTF_8.name())); response.setContentType("application/vnd.ms-excel;charset=UTF-8"); } public static List read(MultipartFile file, Class head) throws IOException { - // 参考 https://t.zsxq.com/zM77F 帖子,增加 try 处理,兼容 windows 场景 - try (InputStream inputStream = file.getInputStream()) { - return FastExcelFactory.read(inputStream, head, null) - .autoCloseStream(false) // 不要自动关闭,交给 Servlet 自己处理 - .doReadAllSync(); - } + return EasyExcel.read(file.getInputStream(), head, null) + .autoCloseStream(false) // 不要自动关闭,交给 Servlet 自己处理 + .doReadAllSync(); } } diff --git a/yudao-module-ai/src/main/java/cn/iocoder/yudao/module/ai/enums/ErrorCodeConstants.java b/yudao-module-ai/src/main/java/cn/iocoder/yudao/module/ai/enums/ErrorCodeConstants.java index 8a8a388..64464dd 100644 --- a/yudao-module-ai/src/main/java/cn/iocoder/yudao/module/ai/enums/ErrorCodeConstants.java +++ b/yudao-module-ai/src/main/java/cn/iocoder/yudao/module/ai/enums/ErrorCodeConstants.java @@ -65,4 +65,5 @@ public interface ErrorCodeConstants { ErrorCode WORKFLOW_NOT_EXISTS = new ErrorCode(1_040_011_000, "工作流不存在"); ErrorCode WORKFLOW_CODE_EXISTS = new ErrorCode(1_040_011_001, "工作流标识已存在"); + } diff --git a/yudao-module-bpm/src/main/java/cn/iocoder/yudao/module/bpm/controller/admin/definition/vo/expression/BpmProcessExpressionRespVO.java b/yudao-module-bpm/src/main/java/cn/iocoder/yudao/module/bpm/controller/admin/definition/vo/expression/BpmProcessExpressionRespVO.java index 7ce45b3..708f4ca 100644 --- a/yudao-module-bpm/src/main/java/cn/iocoder/yudao/module/bpm/controller/admin/definition/vo/expression/BpmProcessExpressionRespVO.java +++ b/yudao-module-bpm/src/main/java/cn/iocoder/yudao/module/bpm/controller/admin/definition/vo/expression/BpmProcessExpressionRespVO.java @@ -1,6 +1,6 @@ package cn.iocoder.yudao.module.bpm.controller.admin.definition.vo.expression; -import cn.idev.excel.annotation.ExcelProperty; +import com.alibaba.excel.annotation.ExcelProperty; import io.swagger.v3.oas.annotations.media.Schema; import lombok.Data; diff --git a/yudao-module-crm/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/business/vo/business/CrmBusinessRespVO.java b/yudao-module-crm/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/business/vo/business/CrmBusinessRespVO.java index d94c856..49cdcb8 100644 --- a/yudao-module-crm/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/business/vo/business/CrmBusinessRespVO.java +++ b/yudao-module-crm/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/business/vo/business/CrmBusinessRespVO.java @@ -1,7 +1,7 @@ package cn.iocoder.yudao.module.crm.controller.admin.business.vo.business; -import cn.idev.excel.annotation.ExcelIgnoreUnannotated; -import cn.idev.excel.annotation.ExcelProperty; +import com.alibaba.excel.annotation.ExcelIgnoreUnannotated; +import com.alibaba.excel.annotation.ExcelProperty; import io.swagger.v3.oas.annotations.media.Schema; import lombok.AllArgsConstructor; import lombok.Data; diff --git a/yudao-module-crm/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/clue/vo/CrmClueRespVO.java b/yudao-module-crm/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/clue/vo/CrmClueRespVO.java index 911f48d..56e5c25 100644 --- a/yudao-module-crm/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/clue/vo/CrmClueRespVO.java +++ b/yudao-module-crm/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/clue/vo/CrmClueRespVO.java @@ -3,8 +3,8 @@ package cn.iocoder.yudao.module.crm.controller.admin.clue.vo; import cn.iocoder.yudao.framework.excel.core.annotations.DictFormat; import cn.iocoder.yudao.framework.excel.core.convert.DictConvert; import cn.iocoder.yudao.module.infra.enums.DictTypeConstants; -import cn.idev.excel.annotation.ExcelIgnoreUnannotated; -import cn.idev.excel.annotation.ExcelProperty; +import com.alibaba.excel.annotation.ExcelIgnoreUnannotated; +import com.alibaba.excel.annotation.ExcelProperty; import io.swagger.v3.oas.annotations.media.Schema; import lombok.Data; import lombok.ToString; diff --git a/yudao-module-crm/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/contact/vo/CrmContactRespVO.java b/yudao-module-crm/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/contact/vo/CrmContactRespVO.java index e198531..b2b1e83 100644 --- a/yudao-module-crm/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/contact/vo/CrmContactRespVO.java +++ b/yudao-module-crm/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/contact/vo/CrmContactRespVO.java @@ -3,8 +3,8 @@ package cn.iocoder.yudao.module.crm.controller.admin.contact.vo; import cn.iocoder.yudao.framework.excel.core.annotations.DictFormat; import cn.iocoder.yudao.framework.excel.core.convert.DictConvert; import cn.iocoder.yudao.module.infra.enums.DictTypeConstants; -import cn.idev.excel.annotation.ExcelIgnoreUnannotated; -import cn.idev.excel.annotation.ExcelProperty; +import com.alibaba.excel.annotation.ExcelIgnoreUnannotated; +import com.alibaba.excel.annotation.ExcelProperty; import io.swagger.v3.oas.annotations.media.Schema; import lombok.Data; import lombok.ToString; diff --git a/yudao-module-crm/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/contract/vo/contract/CrmContractRespVO.java b/yudao-module-crm/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/contract/vo/contract/CrmContractRespVO.java index 307a747..a01bc11 100644 --- a/yudao-module-crm/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/contract/vo/contract/CrmContractRespVO.java +++ b/yudao-module-crm/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/contract/vo/contract/CrmContractRespVO.java @@ -1,7 +1,7 @@ package cn.iocoder.yudao.module.crm.controller.admin.contract.vo.contract; -import cn.idev.excel.annotation.ExcelIgnoreUnannotated; -import cn.idev.excel.annotation.ExcelProperty; +import com.alibaba.excel.annotation.ExcelIgnoreUnannotated; +import com.alibaba.excel.annotation.ExcelProperty; import io.swagger.v3.oas.annotations.media.Schema; import lombok.AllArgsConstructor; import lombok.Data; diff --git a/yudao-module-crm/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/customer/vo/customer/CrmCustomerImportExcelVO.java b/yudao-module-crm/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/customer/vo/customer/CrmCustomerImportExcelVO.java index 884ba3a..f5f9d37 100644 --- a/yudao-module-crm/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/customer/vo/customer/CrmCustomerImportExcelVO.java +++ b/yudao-module-crm/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/customer/vo/customer/CrmCustomerImportExcelVO.java @@ -5,7 +5,7 @@ import cn.iocoder.yudao.framework.excel.core.annotations.ExcelColumnSelect; import cn.iocoder.yudao.framework.excel.core.convert.AreaConvert; import cn.iocoder.yudao.framework.excel.core.convert.DictConvert; import cn.iocoder.yudao.module.crm.framework.excel.core.AreaExcelColumnSelectFunction; -import cn.idev.excel.annotation.ExcelProperty; +import com.alibaba.excel.annotation.ExcelProperty; import lombok.AllArgsConstructor; import lombok.Builder; import lombok.Data; diff --git a/yudao-module-crm/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/customer/vo/customer/CrmCustomerRespVO.java b/yudao-module-crm/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/customer/vo/customer/CrmCustomerRespVO.java index 1b33728..2361299 100644 --- a/yudao-module-crm/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/customer/vo/customer/CrmCustomerRespVO.java +++ b/yudao-module-crm/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/customer/vo/customer/CrmCustomerRespVO.java @@ -3,8 +3,8 @@ package cn.iocoder.yudao.module.crm.controller.admin.customer.vo.customer; import cn.iocoder.yudao.framework.excel.core.annotations.DictFormat; import cn.iocoder.yudao.framework.excel.core.convert.DictConvert; import cn.iocoder.yudao.module.infra.enums.DictTypeConstants; -import cn.idev.excel.annotation.ExcelIgnoreUnannotated; -import cn.idev.excel.annotation.ExcelProperty; +import com.alibaba.excel.annotation.ExcelIgnoreUnannotated; +import com.alibaba.excel.annotation.ExcelProperty; import io.swagger.v3.oas.annotations.media.Schema; import lombok.Data; diff --git a/yudao-module-crm/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/followup/vo/CrmFollowUpRecordRespVO.java b/yudao-module-crm/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/followup/vo/CrmFollowUpRecordRespVO.java index 075c3b3..2e611bb 100644 --- a/yudao-module-crm/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/followup/vo/CrmFollowUpRecordRespVO.java +++ b/yudao-module-crm/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/followup/vo/CrmFollowUpRecordRespVO.java @@ -2,8 +2,8 @@ package cn.iocoder.yudao.module.crm.controller.admin.followup.vo; import cn.iocoder.yudao.framework.excel.core.annotations.DictFormat; import cn.iocoder.yudao.module.crm.controller.admin.business.vo.business.CrmBusinessRespVO; -import cn.idev.excel.annotation.ExcelIgnoreUnannotated; -import cn.idev.excel.annotation.ExcelProperty; +import com.alibaba.excel.annotation.ExcelIgnoreUnannotated; +import com.alibaba.excel.annotation.ExcelProperty; import io.swagger.v3.oas.annotations.media.Schema; import lombok.Data; diff --git a/yudao-module-crm/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/product/vo/category/CrmProductCategoryListReqVO.java b/yudao-module-crm/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/product/vo/category/CrmProductCategoryListReqVO.java index c8e181e..6144c95 100644 --- a/yudao-module-crm/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/product/vo/category/CrmProductCategoryListReqVO.java +++ b/yudao-module-crm/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/product/vo/category/CrmProductCategoryListReqVO.java @@ -1,6 +1,6 @@ package cn.iocoder.yudao.module.crm.controller.admin.product.vo.category; -import cn.idev.excel.annotation.ExcelProperty; +import com.alibaba.excel.annotation.ExcelProperty; import io.swagger.v3.oas.annotations.media.Schema; import lombok.Data; diff --git a/yudao-module-crm/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/product/vo/product/CrmProductRespVO.java b/yudao-module-crm/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/product/vo/product/CrmProductRespVO.java index 5ef01a3..aa955a9 100644 --- a/yudao-module-crm/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/product/vo/product/CrmProductRespVO.java +++ b/yudao-module-crm/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/product/vo/product/CrmProductRespVO.java @@ -4,8 +4,8 @@ import cn.iocoder.yudao.framework.excel.core.annotations.DictFormat; import cn.iocoder.yudao.framework.excel.core.convert.DictConvert; import cn.iocoder.yudao.module.crm.dal.dataobject.product.CrmProductCategoryDO; import cn.iocoder.yudao.module.crm.enums.DictTypeConstants; -import cn.idev.excel.annotation.ExcelIgnoreUnannotated; -import cn.idev.excel.annotation.ExcelProperty; +import com.alibaba.excel.annotation.ExcelIgnoreUnannotated; +import com.alibaba.excel.annotation.ExcelProperty; import com.fhs.core.trans.anno.Trans; import com.fhs.core.trans.constant.TransType; import com.fhs.core.trans.vo.VO; diff --git a/yudao-module-crm/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/receivable/vo/plan/CrmReceivablePlanRespVO.java b/yudao-module-crm/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/receivable/vo/plan/CrmReceivablePlanRespVO.java index 5feb99c..8962b82 100644 --- a/yudao-module-crm/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/receivable/vo/plan/CrmReceivablePlanRespVO.java +++ b/yudao-module-crm/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/receivable/vo/plan/CrmReceivablePlanRespVO.java @@ -1,8 +1,8 @@ package cn.iocoder.yudao.module.crm.controller.admin.receivable.vo.plan; import cn.iocoder.yudao.module.crm.controller.admin.receivable.vo.receivable.CrmReceivableRespVO; -import cn.idev.excel.annotation.ExcelIgnoreUnannotated; -import cn.idev.excel.annotation.ExcelProperty; +import com.alibaba.excel.annotation.ExcelIgnoreUnannotated; +import com.alibaba.excel.annotation.ExcelProperty; import io.swagger.v3.oas.annotations.media.Schema; import lombok.Data; diff --git a/yudao-module-crm/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/receivable/vo/receivable/CrmReceivableRespVO.java b/yudao-module-crm/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/receivable/vo/receivable/CrmReceivableRespVO.java index 75827db..6d712e3 100644 --- a/yudao-module-crm/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/receivable/vo/receivable/CrmReceivableRespVO.java +++ b/yudao-module-crm/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/receivable/vo/receivable/CrmReceivableRespVO.java @@ -4,8 +4,8 @@ import cn.iocoder.yudao.framework.excel.core.annotations.DictFormat; import cn.iocoder.yudao.framework.excel.core.convert.DictConvert; import cn.iocoder.yudao.module.crm.controller.admin.contract.vo.contract.CrmContractRespVO; import cn.iocoder.yudao.module.crm.enums.DictTypeConstants; -import cn.idev.excel.annotation.ExcelIgnoreUnannotated; -import cn.idev.excel.annotation.ExcelProperty; +import com.alibaba.excel.annotation.ExcelIgnoreUnannotated; +import com.alibaba.excel.annotation.ExcelProperty; import io.swagger.v3.oas.annotations.media.Schema; import lombok.Data; diff --git a/yudao-module-customer/src/main/java/cn/iocoder/yudao/module/cus/controller/admin/management/vo/ManagementRespVO.java b/yudao-module-customer/src/main/java/cn/iocoder/yudao/module/cus/controller/admin/management/vo/ManagementRespVO.java index dceba37..d74a769 100644 --- a/yudao-module-customer/src/main/java/cn/iocoder/yudao/module/cus/controller/admin/management/vo/ManagementRespVO.java +++ b/yudao-module-customer/src/main/java/cn/iocoder/yudao/module/cus/controller/admin/management/vo/ManagementRespVO.java @@ -7,7 +7,8 @@ import java.util.*; import java.math.BigDecimal; import org.springframework.format.annotation.DateTimeFormat; import java.time.LocalDateTime; -import cn.idev.excel.annotation.*; +import com.alibaba.excel.annotation.ExcelIgnoreUnannotated; +import com.alibaba.excel.annotation.ExcelProperty; @Schema(description = "管理后台 - 客户管理 Response VO") @Data diff --git a/yudao-module-erp/src/main/java/cn/iocoder/yudao/module/erp/controller/admin/finance/vo/account/ErpAccountRespVO.java b/yudao-module-erp/src/main/java/cn/iocoder/yudao/module/erp/controller/admin/finance/vo/account/ErpAccountRespVO.java index 3e03169..cd6c961 100644 --- a/yudao-module-erp/src/main/java/cn/iocoder/yudao/module/erp/controller/admin/finance/vo/account/ErpAccountRespVO.java +++ b/yudao-module-erp/src/main/java/cn/iocoder/yudao/module/erp/controller/admin/finance/vo/account/ErpAccountRespVO.java @@ -2,8 +2,8 @@ package cn.iocoder.yudao.module.erp.controller.admin.finance.vo.account; import cn.iocoder.yudao.framework.excel.core.annotations.DictFormat; import cn.iocoder.yudao.module.system.enums.DictTypeConstants; -import cn.idev.excel.annotation.ExcelIgnoreUnannotated; -import cn.idev.excel.annotation.ExcelProperty; +import com.alibaba.excel.annotation.ExcelIgnoreUnannotated; +import com.alibaba.excel.annotation.ExcelProperty; import io.swagger.v3.oas.annotations.media.Schema; import lombok.Data; diff --git a/yudao-module-erp/src/main/java/cn/iocoder/yudao/module/erp/controller/admin/finance/vo/payment/ErpFinancePaymentRespVO.java b/yudao-module-erp/src/main/java/cn/iocoder/yudao/module/erp/controller/admin/finance/vo/payment/ErpFinancePaymentRespVO.java index f04eed1..097c674 100644 --- a/yudao-module-erp/src/main/java/cn/iocoder/yudao/module/erp/controller/admin/finance/vo/payment/ErpFinancePaymentRespVO.java +++ b/yudao-module-erp/src/main/java/cn/iocoder/yudao/module/erp/controller/admin/finance/vo/payment/ErpFinancePaymentRespVO.java @@ -1,7 +1,7 @@ package cn.iocoder.yudao.module.erp.controller.admin.finance.vo.payment; -import cn.idev.excel.annotation.ExcelIgnoreUnannotated; -import cn.idev.excel.annotation.ExcelProperty; +import com.alibaba.excel.annotation.ExcelIgnoreUnannotated; +import com.alibaba.excel.annotation.ExcelProperty; import io.swagger.v3.oas.annotations.media.Schema; import lombok.Data; diff --git a/yudao-module-erp/src/main/java/cn/iocoder/yudao/module/erp/controller/admin/finance/vo/receipt/ErpFinanceReceiptRespVO.java b/yudao-module-erp/src/main/java/cn/iocoder/yudao/module/erp/controller/admin/finance/vo/receipt/ErpFinanceReceiptRespVO.java index 89bfb05..38031ea 100644 --- a/yudao-module-erp/src/main/java/cn/iocoder/yudao/module/erp/controller/admin/finance/vo/receipt/ErpFinanceReceiptRespVO.java +++ b/yudao-module-erp/src/main/java/cn/iocoder/yudao/module/erp/controller/admin/finance/vo/receipt/ErpFinanceReceiptRespVO.java @@ -1,7 +1,7 @@ package cn.iocoder.yudao.module.erp.controller.admin.finance.vo.receipt; -import cn.idev.excel.annotation.ExcelIgnoreUnannotated; -import cn.idev.excel.annotation.ExcelProperty; +import com.alibaba.excel.annotation.ExcelIgnoreUnannotated; +import com.alibaba.excel.annotation.ExcelProperty; import io.swagger.v3.oas.annotations.media.Schema; import lombok.Data; diff --git a/yudao-module-erp/src/main/java/cn/iocoder/yudao/module/erp/controller/admin/product/vo/category/ErpProductCategoryRespVO.java b/yudao-module-erp/src/main/java/cn/iocoder/yudao/module/erp/controller/admin/product/vo/category/ErpProductCategoryRespVO.java index 29d4c2f..53a1188 100644 --- a/yudao-module-erp/src/main/java/cn/iocoder/yudao/module/erp/controller/admin/product/vo/category/ErpProductCategoryRespVO.java +++ b/yudao-module-erp/src/main/java/cn/iocoder/yudao/module/erp/controller/admin/product/vo/category/ErpProductCategoryRespVO.java @@ -3,8 +3,8 @@ package cn.iocoder.yudao.module.erp.controller.admin.product.vo.category; import cn.iocoder.yudao.framework.excel.core.annotations.DictFormat; import cn.iocoder.yudao.framework.excel.core.convert.DictConvert; import cn.iocoder.yudao.module.system.enums.DictTypeConstants; -import cn.idev.excel.annotation.ExcelIgnoreUnannotated; -import cn.idev.excel.annotation.ExcelProperty; +import com.alibaba.excel.annotation.ExcelIgnoreUnannotated; +import com.alibaba.excel.annotation.ExcelProperty; import io.swagger.v3.oas.annotations.media.Schema; import lombok.Data; diff --git a/yudao-module-erp/src/main/java/cn/iocoder/yudao/module/erp/controller/admin/product/vo/product/ErpProductRespVO.java b/yudao-module-erp/src/main/java/cn/iocoder/yudao/module/erp/controller/admin/product/vo/product/ErpProductRespVO.java index 8dcc9f8..3576322 100644 --- a/yudao-module-erp/src/main/java/cn/iocoder/yudao/module/erp/controller/admin/product/vo/product/ErpProductRespVO.java +++ b/yudao-module-erp/src/main/java/cn/iocoder/yudao/module/erp/controller/admin/product/vo/product/ErpProductRespVO.java @@ -1,7 +1,7 @@ package cn.iocoder.yudao.module.erp.controller.admin.product.vo.product; -import cn.idev.excel.annotation.ExcelIgnoreUnannotated; -import cn.idev.excel.annotation.ExcelProperty; +import com.alibaba.excel.annotation.ExcelIgnoreUnannotated; +import com.alibaba.excel.annotation.ExcelProperty; import io.swagger.v3.oas.annotations.media.Schema; import lombok.Data; diff --git a/yudao-module-erp/src/main/java/cn/iocoder/yudao/module/erp/controller/admin/product/vo/unit/ErpProductUnitRespVO.java b/yudao-module-erp/src/main/java/cn/iocoder/yudao/module/erp/controller/admin/product/vo/unit/ErpProductUnitRespVO.java index 3044d5a..4122fbc 100644 --- a/yudao-module-erp/src/main/java/cn/iocoder/yudao/module/erp/controller/admin/product/vo/unit/ErpProductUnitRespVO.java +++ b/yudao-module-erp/src/main/java/cn/iocoder/yudao/module/erp/controller/admin/product/vo/unit/ErpProductUnitRespVO.java @@ -2,8 +2,8 @@ package cn.iocoder.yudao.module.erp.controller.admin.product.vo.unit; import cn.iocoder.yudao.framework.excel.core.annotations.DictFormat; import cn.iocoder.yudao.module.system.enums.DictTypeConstants; -import cn.idev.excel.annotation.ExcelIgnoreUnannotated; -import cn.idev.excel.annotation.ExcelProperty; +import com.alibaba.excel.annotation.ExcelIgnoreUnannotated; +import com.alibaba.excel.annotation.ExcelProperty; import io.swagger.v3.oas.annotations.media.Schema; import lombok.Data; diff --git a/yudao-module-erp/src/main/java/cn/iocoder/yudao/module/erp/controller/admin/purchase/vo/in/ErpPurchaseInRespVO.java b/yudao-module-erp/src/main/java/cn/iocoder/yudao/module/erp/controller/admin/purchase/vo/in/ErpPurchaseInRespVO.java index 277fef6..a781768 100644 --- a/yudao-module-erp/src/main/java/cn/iocoder/yudao/module/erp/controller/admin/purchase/vo/in/ErpPurchaseInRespVO.java +++ b/yudao-module-erp/src/main/java/cn/iocoder/yudao/module/erp/controller/admin/purchase/vo/in/ErpPurchaseInRespVO.java @@ -1,7 +1,7 @@ package cn.iocoder.yudao.module.erp.controller.admin.purchase.vo.in; -import cn.idev.excel.annotation.ExcelIgnoreUnannotated; -import cn.idev.excel.annotation.ExcelProperty; +import com.alibaba.excel.annotation.ExcelIgnoreUnannotated; +import com.alibaba.excel.annotation.ExcelProperty; import io.swagger.v3.oas.annotations.media.Schema; import lombok.Data; diff --git a/yudao-module-erp/src/main/java/cn/iocoder/yudao/module/erp/controller/admin/purchase/vo/order/ErpPurchaseOrderRespVO.java b/yudao-module-erp/src/main/java/cn/iocoder/yudao/module/erp/controller/admin/purchase/vo/order/ErpPurchaseOrderRespVO.java index 27ac705..cc9da4d 100644 --- a/yudao-module-erp/src/main/java/cn/iocoder/yudao/module/erp/controller/admin/purchase/vo/order/ErpPurchaseOrderRespVO.java +++ b/yudao-module-erp/src/main/java/cn/iocoder/yudao/module/erp/controller/admin/purchase/vo/order/ErpPurchaseOrderRespVO.java @@ -1,7 +1,7 @@ package cn.iocoder.yudao.module.erp.controller.admin.purchase.vo.order; -import cn.idev.excel.annotation.ExcelIgnoreUnannotated; -import cn.idev.excel.annotation.ExcelProperty; +import com.alibaba.excel.annotation.ExcelIgnoreUnannotated; +import com.alibaba.excel.annotation.ExcelProperty; import io.swagger.v3.oas.annotations.media.Schema; import lombok.Data; diff --git a/yudao-module-erp/src/main/java/cn/iocoder/yudao/module/erp/controller/admin/purchase/vo/returns/ErpPurchaseReturnRespVO.java b/yudao-module-erp/src/main/java/cn/iocoder/yudao/module/erp/controller/admin/purchase/vo/returns/ErpPurchaseReturnRespVO.java index fd639a0..8546d05 100644 --- a/yudao-module-erp/src/main/java/cn/iocoder/yudao/module/erp/controller/admin/purchase/vo/returns/ErpPurchaseReturnRespVO.java +++ b/yudao-module-erp/src/main/java/cn/iocoder/yudao/module/erp/controller/admin/purchase/vo/returns/ErpPurchaseReturnRespVO.java @@ -1,7 +1,7 @@ package cn.iocoder.yudao.module.erp.controller.admin.purchase.vo.returns; -import cn.idev.excel.annotation.ExcelIgnoreUnannotated; -import cn.idev.excel.annotation.ExcelProperty; +import com.alibaba.excel.annotation.ExcelIgnoreUnannotated; +import com.alibaba.excel.annotation.ExcelProperty; import io.swagger.v3.oas.annotations.media.Schema; import lombok.Data; diff --git a/yudao-module-erp/src/main/java/cn/iocoder/yudao/module/erp/controller/admin/purchase/vo/supplier/ErpSupplierRespVO.java b/yudao-module-erp/src/main/java/cn/iocoder/yudao/module/erp/controller/admin/purchase/vo/supplier/ErpSupplierRespVO.java index 538a3b1..2ecd2bb 100644 --- a/yudao-module-erp/src/main/java/cn/iocoder/yudao/module/erp/controller/admin/purchase/vo/supplier/ErpSupplierRespVO.java +++ b/yudao-module-erp/src/main/java/cn/iocoder/yudao/module/erp/controller/admin/purchase/vo/supplier/ErpSupplierRespVO.java @@ -3,8 +3,8 @@ package cn.iocoder.yudao.module.erp.controller.admin.purchase.vo.supplier; import cn.iocoder.yudao.framework.excel.core.annotations.DictFormat; import cn.iocoder.yudao.framework.excel.core.convert.DictConvert; import cn.iocoder.yudao.module.system.enums.DictTypeConstants; -import cn.idev.excel.annotation.ExcelIgnoreUnannotated; -import cn.idev.excel.annotation.ExcelProperty; +import com.alibaba.excel.annotation.ExcelIgnoreUnannotated; +import com.alibaba.excel.annotation.ExcelProperty; import io.swagger.v3.oas.annotations.media.Schema; import lombok.Data; diff --git a/yudao-module-erp/src/main/java/cn/iocoder/yudao/module/erp/controller/admin/sale/vo/customer/ErpCustomerRespVO.java b/yudao-module-erp/src/main/java/cn/iocoder/yudao/module/erp/controller/admin/sale/vo/customer/ErpCustomerRespVO.java index c75a99a..e1a0436 100644 --- a/yudao-module-erp/src/main/java/cn/iocoder/yudao/module/erp/controller/admin/sale/vo/customer/ErpCustomerRespVO.java +++ b/yudao-module-erp/src/main/java/cn/iocoder/yudao/module/erp/controller/admin/sale/vo/customer/ErpCustomerRespVO.java @@ -1,10 +1,11 @@ package cn.iocoder.yudao.module.erp.controller.admin.sale.vo.customer; +import com.alibaba.excel.annotation.ExcelIgnoreUnannotated; +import com.alibaba.excel.annotation.ExcelProperty; import io.swagger.v3.oas.annotations.media.Schema; import lombok.*; import java.math.BigDecimal; import java.time.LocalDateTime; -import cn.idev.excel.annotation.*; import cn.iocoder.yudao.framework.excel.core.annotations.DictFormat; import cn.iocoder.yudao.framework.excel.core.convert.DictConvert; diff --git a/yudao-module-erp/src/main/java/cn/iocoder/yudao/module/erp/controller/admin/sale/vo/order/ErpSaleOrderRespVO.java b/yudao-module-erp/src/main/java/cn/iocoder/yudao/module/erp/controller/admin/sale/vo/order/ErpSaleOrderRespVO.java index 8de5e9c..c6fb41b 100644 --- a/yudao-module-erp/src/main/java/cn/iocoder/yudao/module/erp/controller/admin/sale/vo/order/ErpSaleOrderRespVO.java +++ b/yudao-module-erp/src/main/java/cn/iocoder/yudao/module/erp/controller/admin/sale/vo/order/ErpSaleOrderRespVO.java @@ -1,7 +1,7 @@ package cn.iocoder.yudao.module.erp.controller.admin.sale.vo.order; -import cn.idev.excel.annotation.ExcelIgnoreUnannotated; -import cn.idev.excel.annotation.ExcelProperty; +import com.alibaba.excel.annotation.ExcelIgnoreUnannotated; +import com.alibaba.excel.annotation.ExcelProperty; import io.swagger.v3.oas.annotations.media.Schema; import lombok.Data; diff --git a/yudao-module-erp/src/main/java/cn/iocoder/yudao/module/erp/controller/admin/sale/vo/out/ErpSaleOutRespVO.java b/yudao-module-erp/src/main/java/cn/iocoder/yudao/module/erp/controller/admin/sale/vo/out/ErpSaleOutRespVO.java index 6d44e14..635ef42 100644 --- a/yudao-module-erp/src/main/java/cn/iocoder/yudao/module/erp/controller/admin/sale/vo/out/ErpSaleOutRespVO.java +++ b/yudao-module-erp/src/main/java/cn/iocoder/yudao/module/erp/controller/admin/sale/vo/out/ErpSaleOutRespVO.java @@ -1,7 +1,7 @@ package cn.iocoder.yudao.module.erp.controller.admin.sale.vo.out; -import cn.idev.excel.annotation.ExcelIgnoreUnannotated; -import cn.idev.excel.annotation.ExcelProperty; +import com.alibaba.excel.annotation.ExcelIgnoreUnannotated; +import com.alibaba.excel.annotation.ExcelProperty; import io.swagger.v3.oas.annotations.media.Schema; import lombok.Data; diff --git a/yudao-module-erp/src/main/java/cn/iocoder/yudao/module/erp/controller/admin/sale/vo/returns/ErpSaleReturnRespVO.java b/yudao-module-erp/src/main/java/cn/iocoder/yudao/module/erp/controller/admin/sale/vo/returns/ErpSaleReturnRespVO.java index 41e5828..0715830 100644 --- a/yudao-module-erp/src/main/java/cn/iocoder/yudao/module/erp/controller/admin/sale/vo/returns/ErpSaleReturnRespVO.java +++ b/yudao-module-erp/src/main/java/cn/iocoder/yudao/module/erp/controller/admin/sale/vo/returns/ErpSaleReturnRespVO.java @@ -1,7 +1,7 @@ package cn.iocoder.yudao.module.erp.controller.admin.sale.vo.returns; -import cn.idev.excel.annotation.ExcelIgnoreUnannotated; -import cn.idev.excel.annotation.ExcelProperty; +import com.alibaba.excel.annotation.ExcelIgnoreUnannotated; +import com.alibaba.excel.annotation.ExcelProperty; import io.swagger.v3.oas.annotations.media.Schema; import javax.validation.constraints.NotNull; import lombok.Data; diff --git a/yudao-module-erp/src/main/java/cn/iocoder/yudao/module/erp/controller/admin/stock/vo/check/ErpStockCheckRespVO.java b/yudao-module-erp/src/main/java/cn/iocoder/yudao/module/erp/controller/admin/stock/vo/check/ErpStockCheckRespVO.java index ccc3efe..f557225 100644 --- a/yudao-module-erp/src/main/java/cn/iocoder/yudao/module/erp/controller/admin/stock/vo/check/ErpStockCheckRespVO.java +++ b/yudao-module-erp/src/main/java/cn/iocoder/yudao/module/erp/controller/admin/stock/vo/check/ErpStockCheckRespVO.java @@ -2,8 +2,8 @@ package cn.iocoder.yudao.module.erp.controller.admin.stock.vo.check; import cn.iocoder.yudao.framework.excel.core.annotations.DictFormat; import cn.iocoder.yudao.framework.excel.core.convert.DictConvert; -import cn.idev.excel.annotation.ExcelIgnoreUnannotated; -import cn.idev.excel.annotation.ExcelProperty; +import com.alibaba.excel.annotation.ExcelIgnoreUnannotated; +import com.alibaba.excel.annotation.ExcelProperty; import io.swagger.v3.oas.annotations.media.Schema; import javax.validation.constraints.NotNull; import lombok.Data; diff --git a/yudao-module-erp/src/main/java/cn/iocoder/yudao/module/erp/controller/admin/stock/vo/in/ErpStockInRespVO.java b/yudao-module-erp/src/main/java/cn/iocoder/yudao/module/erp/controller/admin/stock/vo/in/ErpStockInRespVO.java index 1d23f36..74545c3 100644 --- a/yudao-module-erp/src/main/java/cn/iocoder/yudao/module/erp/controller/admin/stock/vo/in/ErpStockInRespVO.java +++ b/yudao-module-erp/src/main/java/cn/iocoder/yudao/module/erp/controller/admin/stock/vo/in/ErpStockInRespVO.java @@ -2,8 +2,8 @@ package cn.iocoder.yudao.module.erp.controller.admin.stock.vo.in; import cn.iocoder.yudao.framework.excel.core.annotations.DictFormat; import cn.iocoder.yudao.framework.excel.core.convert.DictConvert; -import cn.idev.excel.annotation.ExcelIgnoreUnannotated; -import cn.idev.excel.annotation.ExcelProperty; +import com.alibaba.excel.annotation.ExcelIgnoreUnannotated; +import com.alibaba.excel.annotation.ExcelProperty; import io.swagger.v3.oas.annotations.media.Schema; import lombok.Data; diff --git a/yudao-module-erp/src/main/java/cn/iocoder/yudao/module/erp/controller/admin/stock/vo/move/ErpStockMoveRespVO.java b/yudao-module-erp/src/main/java/cn/iocoder/yudao/module/erp/controller/admin/stock/vo/move/ErpStockMoveRespVO.java index f5f7650..9902c06 100644 --- a/yudao-module-erp/src/main/java/cn/iocoder/yudao/module/erp/controller/admin/stock/vo/move/ErpStockMoveRespVO.java +++ b/yudao-module-erp/src/main/java/cn/iocoder/yudao/module/erp/controller/admin/stock/vo/move/ErpStockMoveRespVO.java @@ -2,8 +2,8 @@ package cn.iocoder.yudao.module.erp.controller.admin.stock.vo.move; import cn.iocoder.yudao.framework.excel.core.annotations.DictFormat; import cn.iocoder.yudao.framework.excel.core.convert.DictConvert; -import cn.idev.excel.annotation.ExcelIgnoreUnannotated; -import cn.idev.excel.annotation.ExcelProperty; +import com.alibaba.excel.annotation.ExcelIgnoreUnannotated; +import com.alibaba.excel.annotation.ExcelProperty; import io.swagger.v3.oas.annotations.media.Schema; import lombok.Data; diff --git a/yudao-module-erp/src/main/java/cn/iocoder/yudao/module/erp/controller/admin/stock/vo/out/ErpStockOutRespVO.java b/yudao-module-erp/src/main/java/cn/iocoder/yudao/module/erp/controller/admin/stock/vo/out/ErpStockOutRespVO.java index a1d0f51..321116f 100644 --- a/yudao-module-erp/src/main/java/cn/iocoder/yudao/module/erp/controller/admin/stock/vo/out/ErpStockOutRespVO.java +++ b/yudao-module-erp/src/main/java/cn/iocoder/yudao/module/erp/controller/admin/stock/vo/out/ErpStockOutRespVO.java @@ -2,8 +2,8 @@ package cn.iocoder.yudao.module.erp.controller.admin.stock.vo.out; import cn.iocoder.yudao.framework.excel.core.annotations.DictFormat; import cn.iocoder.yudao.framework.excel.core.convert.DictConvert; -import cn.idev.excel.annotation.ExcelIgnoreUnannotated; -import cn.idev.excel.annotation.ExcelProperty; +import com.alibaba.excel.annotation.ExcelIgnoreUnannotated; +import com.alibaba.excel.annotation.ExcelProperty; import io.swagger.v3.oas.annotations.media.Schema; import lombok.Data; diff --git a/yudao-module-erp/src/main/java/cn/iocoder/yudao/module/erp/controller/admin/stock/vo/record/ErpStockRecordRespVO.java b/yudao-module-erp/src/main/java/cn/iocoder/yudao/module/erp/controller/admin/stock/vo/record/ErpStockRecordRespVO.java index 47e1261..70f65c9 100644 --- a/yudao-module-erp/src/main/java/cn/iocoder/yudao/module/erp/controller/admin/stock/vo/record/ErpStockRecordRespVO.java +++ b/yudao-module-erp/src/main/java/cn/iocoder/yudao/module/erp/controller/admin/stock/vo/record/ErpStockRecordRespVO.java @@ -3,8 +3,8 @@ package cn.iocoder.yudao.module.erp.controller.admin.stock.vo.record; import cn.iocoder.yudao.framework.excel.core.annotations.DictFormat; import cn.iocoder.yudao.framework.excel.core.convert.DictConvert; import cn.iocoder.yudao.module.erp.enums.DictTypeConstants; -import cn.idev.excel.annotation.ExcelIgnoreUnannotated; -import cn.idev.excel.annotation.ExcelProperty; +import com.alibaba.excel.annotation.ExcelIgnoreUnannotated; +import com.alibaba.excel.annotation.ExcelProperty; import io.swagger.v3.oas.annotations.media.Schema; import lombok.Data; diff --git a/yudao-module-erp/src/main/java/cn/iocoder/yudao/module/erp/controller/admin/stock/vo/stock/ErpStockRespVO.java b/yudao-module-erp/src/main/java/cn/iocoder/yudao/module/erp/controller/admin/stock/vo/stock/ErpStockRespVO.java index 8d84789..f88ea70 100644 --- a/yudao-module-erp/src/main/java/cn/iocoder/yudao/module/erp/controller/admin/stock/vo/stock/ErpStockRespVO.java +++ b/yudao-module-erp/src/main/java/cn/iocoder/yudao/module/erp/controller/admin/stock/vo/stock/ErpStockRespVO.java @@ -1,7 +1,7 @@ package cn.iocoder.yudao.module.erp.controller.admin.stock.vo.stock; -import cn.idev.excel.annotation.ExcelIgnoreUnannotated; -import cn.idev.excel.annotation.ExcelProperty; +import com.alibaba.excel.annotation.ExcelIgnoreUnannotated; +import com.alibaba.excel.annotation.ExcelProperty; import io.swagger.v3.oas.annotations.media.Schema; import lombok.Data; diff --git a/yudao-module-erp/src/main/java/cn/iocoder/yudao/module/erp/controller/admin/stock/vo/warehouse/ErpWarehouseRespVO.java b/yudao-module-erp/src/main/java/cn/iocoder/yudao/module/erp/controller/admin/stock/vo/warehouse/ErpWarehouseRespVO.java index 06825b8..394e682 100644 --- a/yudao-module-erp/src/main/java/cn/iocoder/yudao/module/erp/controller/admin/stock/vo/warehouse/ErpWarehouseRespVO.java +++ b/yudao-module-erp/src/main/java/cn/iocoder/yudao/module/erp/controller/admin/stock/vo/warehouse/ErpWarehouseRespVO.java @@ -3,8 +3,8 @@ package cn.iocoder.yudao.module.erp.controller.admin.stock.vo.warehouse; import cn.iocoder.yudao.framework.excel.core.annotations.DictFormat; import cn.iocoder.yudao.framework.excel.core.convert.DictConvert; import cn.iocoder.yudao.module.system.enums.DictTypeConstants; -import cn.idev.excel.annotation.ExcelIgnoreUnannotated; -import cn.idev.excel.annotation.ExcelProperty; +import com.alibaba.excel.annotation.ExcelIgnoreUnannotated; +import com.alibaba.excel.annotation.ExcelProperty; import io.swagger.v3.oas.annotations.media.Schema; import lombok.Data; diff --git a/yudao-module-infra/src/main/java/cn/iocoder/yudao/module/infra/controller/admin/config/vo/ConfigRespVO.java b/yudao-module-infra/src/main/java/cn/iocoder/yudao/module/infra/controller/admin/config/vo/ConfigRespVO.java index 622bb04..a1bd8ef 100644 --- a/yudao-module-infra/src/main/java/cn/iocoder/yudao/module/infra/controller/admin/config/vo/ConfigRespVO.java +++ b/yudao-module-infra/src/main/java/cn/iocoder/yudao/module/infra/controller/admin/config/vo/ConfigRespVO.java @@ -3,8 +3,9 @@ package cn.iocoder.yudao.module.infra.controller.admin.config.vo; import cn.iocoder.yudao.framework.excel.core.annotations.DictFormat; import cn.iocoder.yudao.framework.excel.core.convert.DictConvert; import cn.iocoder.yudao.module.infra.enums.DictTypeConstants; -import cn.idev.excel.annotation.ExcelIgnoreUnannotated; -import cn.idev.excel.annotation.ExcelProperty; + +import com.alibaba.excel.annotation.ExcelIgnoreUnannotated; +import com.alibaba.excel.annotation.ExcelProperty; import io.swagger.v3.oas.annotations.media.Schema; import lombok.Data; diff --git a/yudao-module-infra/src/main/java/cn/iocoder/yudao/module/infra/controller/admin/demo/demo01/vo/Demo01ContactRespVO.java b/yudao-module-infra/src/main/java/cn/iocoder/yudao/module/infra/controller/admin/demo/demo01/vo/Demo01ContactRespVO.java index df0ef60..0d2b343 100644 --- a/yudao-module-infra/src/main/java/cn/iocoder/yudao/module/infra/controller/admin/demo/demo01/vo/Demo01ContactRespVO.java +++ b/yudao-module-infra/src/main/java/cn/iocoder/yudao/module/infra/controller/admin/demo/demo01/vo/Demo01ContactRespVO.java @@ -2,8 +2,9 @@ package cn.iocoder.yudao.module.infra.controller.admin.demo.demo01.vo; import cn.iocoder.yudao.framework.excel.core.annotations.DictFormat; import cn.iocoder.yudao.framework.excel.core.convert.DictConvert; -import cn.idev.excel.annotation.ExcelIgnoreUnannotated; -import cn.idev.excel.annotation.ExcelProperty; + +import com.alibaba.excel.annotation.ExcelIgnoreUnannotated; +import com.alibaba.excel.annotation.ExcelProperty; import io.swagger.v3.oas.annotations.media.Schema; import lombok.Data; diff --git a/yudao-module-infra/src/main/java/cn/iocoder/yudao/module/infra/controller/admin/demo/demo02/vo/Demo02CategoryRespVO.java b/yudao-module-infra/src/main/java/cn/iocoder/yudao/module/infra/controller/admin/demo/demo02/vo/Demo02CategoryRespVO.java index bac533b..0b786d4 100644 --- a/yudao-module-infra/src/main/java/cn/iocoder/yudao/module/infra/controller/admin/demo/demo02/vo/Demo02CategoryRespVO.java +++ b/yudao-module-infra/src/main/java/cn/iocoder/yudao/module/infra/controller/admin/demo/demo02/vo/Demo02CategoryRespVO.java @@ -1,7 +1,8 @@ package cn.iocoder.yudao.module.infra.controller.admin.demo.demo02.vo; -import cn.idev.excel.annotation.ExcelIgnoreUnannotated; -import cn.idev.excel.annotation.ExcelProperty; + +import com.alibaba.excel.annotation.ExcelIgnoreUnannotated; +import com.alibaba.excel.annotation.ExcelProperty; import io.swagger.v3.oas.annotations.media.Schema; import lombok.Data; diff --git a/yudao-module-infra/src/main/java/cn/iocoder/yudao/module/infra/controller/admin/demo/demo03/erp/vo/Demo03StudentErpRespVO.java b/yudao-module-infra/src/main/java/cn/iocoder/yudao/module/infra/controller/admin/demo/demo03/erp/vo/Demo03StudentErpRespVO.java index 3094484..6383959 100644 --- a/yudao-module-infra/src/main/java/cn/iocoder/yudao/module/infra/controller/admin/demo/demo03/erp/vo/Demo03StudentErpRespVO.java +++ b/yudao-module-infra/src/main/java/cn/iocoder/yudao/module/infra/controller/admin/demo/demo03/erp/vo/Demo03StudentErpRespVO.java @@ -2,8 +2,8 @@ package cn.iocoder.yudao.module.infra.controller.admin.demo.demo03.erp.vo; import cn.iocoder.yudao.framework.excel.core.annotations.DictFormat; import cn.iocoder.yudao.framework.excel.core.convert.DictConvert; -import cn.idev.excel.annotation.ExcelIgnoreUnannotated; -import cn.idev.excel.annotation.ExcelProperty; +import com.alibaba.excel.annotation.ExcelIgnoreUnannotated; +import com.alibaba.excel.annotation.ExcelProperty; import io.swagger.v3.oas.annotations.media.Schema; import lombok.Data; diff --git a/yudao-module-infra/src/main/java/cn/iocoder/yudao/module/infra/controller/admin/demo/demo03/inner/vo/Demo03StudentInnerRespVO.java b/yudao-module-infra/src/main/java/cn/iocoder/yudao/module/infra/controller/admin/demo/demo03/inner/vo/Demo03StudentInnerRespVO.java index 3db315b..0150fa9 100644 --- a/yudao-module-infra/src/main/java/cn/iocoder/yudao/module/infra/controller/admin/demo/demo03/inner/vo/Demo03StudentInnerRespVO.java +++ b/yudao-module-infra/src/main/java/cn/iocoder/yudao/module/infra/controller/admin/demo/demo03/inner/vo/Demo03StudentInnerRespVO.java @@ -2,8 +2,8 @@ package cn.iocoder.yudao.module.infra.controller.admin.demo.demo03.inner.vo; import cn.iocoder.yudao.framework.excel.core.annotations.DictFormat; import cn.iocoder.yudao.framework.excel.core.convert.DictConvert; -import cn.idev.excel.annotation.ExcelIgnoreUnannotated; -import cn.idev.excel.annotation.ExcelProperty; +import com.alibaba.excel.annotation.ExcelIgnoreUnannotated; +import com.alibaba.excel.annotation.ExcelProperty; import io.swagger.v3.oas.annotations.media.Schema; import lombok.Data; diff --git a/yudao-module-infra/src/main/java/cn/iocoder/yudao/module/infra/controller/admin/demo/demo03/normal/vo/Demo03StudentNormalRespVO.java b/yudao-module-infra/src/main/java/cn/iocoder/yudao/module/infra/controller/admin/demo/demo03/normal/vo/Demo03StudentNormalRespVO.java index f100c80..d51b77f 100644 --- a/yudao-module-infra/src/main/java/cn/iocoder/yudao/module/infra/controller/admin/demo/demo03/normal/vo/Demo03StudentNormalRespVO.java +++ b/yudao-module-infra/src/main/java/cn/iocoder/yudao/module/infra/controller/admin/demo/demo03/normal/vo/Demo03StudentNormalRespVO.java @@ -2,8 +2,8 @@ package cn.iocoder.yudao.module.infra.controller.admin.demo.demo03.normal.vo; import cn.iocoder.yudao.framework.excel.core.annotations.DictFormat; import cn.iocoder.yudao.framework.excel.core.convert.DictConvert; -import cn.idev.excel.annotation.ExcelIgnoreUnannotated; -import cn.idev.excel.annotation.ExcelProperty; +import com.alibaba.excel.annotation.ExcelIgnoreUnannotated; +import com.alibaba.excel.annotation.ExcelProperty; import io.swagger.v3.oas.annotations.media.Schema; import lombok.Data; diff --git a/yudao-module-infra/src/main/java/cn/iocoder/yudao/module/infra/controller/admin/job/vo/job/JobRespVO.java b/yudao-module-infra/src/main/java/cn/iocoder/yudao/module/infra/controller/admin/job/vo/job/JobRespVO.java index 520a91c..25683f8 100644 --- a/yudao-module-infra/src/main/java/cn/iocoder/yudao/module/infra/controller/admin/job/vo/job/JobRespVO.java +++ b/yudao-module-infra/src/main/java/cn/iocoder/yudao/module/infra/controller/admin/job/vo/job/JobRespVO.java @@ -3,8 +3,8 @@ package cn.iocoder.yudao.module.infra.controller.admin.job.vo.job; import cn.iocoder.yudao.framework.excel.core.annotations.DictFormat; import cn.iocoder.yudao.framework.excel.core.convert.DictConvert; import cn.iocoder.yudao.module.infra.enums.DictTypeConstants; -import cn.idev.excel.annotation.ExcelIgnoreUnannotated; -import cn.idev.excel.annotation.ExcelProperty; +import com.alibaba.excel.annotation.ExcelIgnoreUnannotated; +import com.alibaba.excel.annotation.ExcelProperty; import io.swagger.v3.oas.annotations.media.Schema; import lombok.Data; diff --git a/yudao-module-infra/src/main/java/cn/iocoder/yudao/module/infra/controller/admin/job/vo/log/JobLogRespVO.java b/yudao-module-infra/src/main/java/cn/iocoder/yudao/module/infra/controller/admin/job/vo/log/JobLogRespVO.java index 1d33cdf..2887a48 100644 --- a/yudao-module-infra/src/main/java/cn/iocoder/yudao/module/infra/controller/admin/job/vo/log/JobLogRespVO.java +++ b/yudao-module-infra/src/main/java/cn/iocoder/yudao/module/infra/controller/admin/job/vo/log/JobLogRespVO.java @@ -3,8 +3,8 @@ package cn.iocoder.yudao.module.infra.controller.admin.job.vo.log; import cn.iocoder.yudao.framework.excel.core.annotations.DictFormat; import cn.iocoder.yudao.framework.excel.core.convert.DictConvert; import cn.iocoder.yudao.module.infra.enums.DictTypeConstants; -import cn.idev.excel.annotation.ExcelIgnoreUnannotated; -import cn.idev.excel.annotation.ExcelProperty; +import com.alibaba.excel.annotation.ExcelIgnoreUnannotated; +import com.alibaba.excel.annotation.ExcelProperty; import io.swagger.v3.oas.annotations.media.Schema; import lombok.Data; diff --git a/yudao-module-infra/src/main/java/cn/iocoder/yudao/module/infra/controller/admin/logger/vo/apiaccesslog/ApiAccessLogRespVO.java b/yudao-module-infra/src/main/java/cn/iocoder/yudao/module/infra/controller/admin/logger/vo/apiaccesslog/ApiAccessLogRespVO.java index 1b3dc0f..e1cd0be 100644 --- a/yudao-module-infra/src/main/java/cn/iocoder/yudao/module/infra/controller/admin/logger/vo/apiaccesslog/ApiAccessLogRespVO.java +++ b/yudao-module-infra/src/main/java/cn/iocoder/yudao/module/infra/controller/admin/logger/vo/apiaccesslog/ApiAccessLogRespVO.java @@ -3,8 +3,8 @@ package cn.iocoder.yudao.module.infra.controller.admin.logger.vo.apiaccesslog; import cn.iocoder.yudao.framework.excel.core.annotations.DictFormat; import cn.iocoder.yudao.framework.excel.core.convert.DictConvert; import cn.iocoder.yudao.module.infra.enums.DictTypeConstants; -import cn.idev.excel.annotation.ExcelIgnoreUnannotated; -import cn.idev.excel.annotation.ExcelProperty; +import com.alibaba.excel.annotation.ExcelIgnoreUnannotated; +import com.alibaba.excel.annotation.ExcelProperty; import io.swagger.v3.oas.annotations.media.Schema; import lombok.Data; diff --git a/yudao-module-infra/src/main/java/cn/iocoder/yudao/module/infra/controller/admin/logger/vo/apierrorlog/ApiErrorLogRespVO.java b/yudao-module-infra/src/main/java/cn/iocoder/yudao/module/infra/controller/admin/logger/vo/apierrorlog/ApiErrorLogRespVO.java index 7097924..a3f6f0e 100644 --- a/yudao-module-infra/src/main/java/cn/iocoder/yudao/module/infra/controller/admin/logger/vo/apierrorlog/ApiErrorLogRespVO.java +++ b/yudao-module-infra/src/main/java/cn/iocoder/yudao/module/infra/controller/admin/logger/vo/apierrorlog/ApiErrorLogRespVO.java @@ -3,8 +3,8 @@ package cn.iocoder.yudao.module.infra.controller.admin.logger.vo.apierrorlog; import cn.iocoder.yudao.framework.excel.core.annotations.DictFormat; import cn.iocoder.yudao.framework.excel.core.convert.DictConvert; import cn.iocoder.yudao.module.infra.enums.DictTypeConstants; -import cn.idev.excel.annotation.ExcelIgnoreUnannotated; -import cn.idev.excel.annotation.ExcelProperty; +import com.alibaba.excel.annotation.ExcelIgnoreUnannotated; +import com.alibaba.excel.annotation.ExcelProperty; import io.swagger.v3.oas.annotations.media.Schema; import lombok.Data; diff --git a/yudao-module-infra/src/main/resources/codegen/java/controller/vo/respVO.vm b/yudao-module-infra/src/main/resources/codegen/java/controller/vo/respVO.vm index eae083a..3151665 100644 --- a/yudao-module-infra/src/main/resources/codegen/java/controller/vo/respVO.vm +++ b/yudao-module-infra/src/main/resources/codegen/java/controller/vo/respVO.vm @@ -19,7 +19,8 @@ import java.time.LocalDateTime; #end #end ## 处理 Excel 导出 -import cn.idev.excel.annotation.*; +import com.alibaba.excel.annotation.ExcelIgnoreUnannotated; +import com.alibaba.excel.annotation.ExcelProperty; #foreach ($column in $columns) #if ("$!column.dictType" != "")## 有设置数据字典 import ${DictFormatClassName}; diff --git a/yudao-module-infra/src/main/resources/codegen/java/dal/do.vm b/yudao-module-infra/src/main/resources/codegen/java/dal/do.vm index f15ada4..230361e 100644 --- a/yudao-module-infra/src/main/resources/codegen/java/dal/do.vm +++ b/yudao-module-infra/src/main/resources/codegen/java/dal/do.vm @@ -15,7 +15,8 @@ import ${BaseDOClassName}; ## 处理 Excel 导出 + Schema 注解(仅 DO 模式) #if ($voType == 20) import io.swagger.v3.oas.annotations.media.Schema; -import cn.idev.excel.annotation.*; +import com.alibaba.excel.annotation.ExcelIgnoreUnannotated; +import com.alibaba.excel.annotation.ExcelProperty; #foreach ($column in $columns) #if ("$!column.dictType" != "")## 有设置数据字典 import ${DictFormatClassName}; diff --git a/yudao-module-infra/src/test/resources/codegen/windows10/vue2_master_erp/java/InfraStudentRespVO b/yudao-module-infra/src/test/resources/codegen/windows10/vue2_master_erp/java/InfraStudentRespVO index f86f679..5c15954 100644 --- a/yudao-module-infra/src/test/resources/codegen/windows10/vue2_master_erp/java/InfraStudentRespVO +++ b/yudao-module-infra/src/test/resources/codegen/windows10/vue2_master_erp/java/InfraStudentRespVO @@ -6,7 +6,8 @@ import java.util.*; import java.util.*; import org.springframework.format.annotation.DateTimeFormat; import java.time.LocalDateTime; -import cn.idev.excel.annotation.*; +import com.alibaba.excel.annotation.ExcelIgnoreUnannotated; +import com.alibaba.excel.annotation.ExcelProperty; import cn.iocoder.yudao.framework.excel.core.annotations.DictFormat; import cn.iocoder.yudao.framework.excel.core.convert.DictConvert; diff --git a/yudao-module-infra/src/test/resources/codegen/windows10/vue2_master_inner/java/InfraStudentRespVO b/yudao-module-infra/src/test/resources/codegen/windows10/vue2_master_inner/java/InfraStudentRespVO index f86f679..5c15954 100644 --- a/yudao-module-infra/src/test/resources/codegen/windows10/vue2_master_inner/java/InfraStudentRespVO +++ b/yudao-module-infra/src/test/resources/codegen/windows10/vue2_master_inner/java/InfraStudentRespVO @@ -6,7 +6,8 @@ import java.util.*; import java.util.*; import org.springframework.format.annotation.DateTimeFormat; import java.time.LocalDateTime; -import cn.idev.excel.annotation.*; +import com.alibaba.excel.annotation.ExcelIgnoreUnannotated; +import com.alibaba.excel.annotation.ExcelProperty; import cn.iocoder.yudao.framework.excel.core.annotations.DictFormat; import cn.iocoder.yudao.framework.excel.core.convert.DictConvert; diff --git a/yudao-module-infra/src/test/resources/codegen/windows10/vue2_master_normal/java/InfraStudentRespVO b/yudao-module-infra/src/test/resources/codegen/windows10/vue2_master_normal/java/InfraStudentRespVO index f86f679..5c15954 100644 --- a/yudao-module-infra/src/test/resources/codegen/windows10/vue2_master_normal/java/InfraStudentRespVO +++ b/yudao-module-infra/src/test/resources/codegen/windows10/vue2_master_normal/java/InfraStudentRespVO @@ -6,7 +6,8 @@ import java.util.*; import java.util.*; import org.springframework.format.annotation.DateTimeFormat; import java.time.LocalDateTime; -import cn.idev.excel.annotation.*; +import com.alibaba.excel.annotation.ExcelIgnoreUnannotated; +import com.alibaba.excel.annotation.ExcelProperty; import cn.iocoder.yudao.framework.excel.core.annotations.DictFormat; import cn.iocoder.yudao.framework.excel.core.convert.DictConvert; diff --git a/yudao-module-infra/src/test/resources/codegen/windows10/vue2_one/java/InfraStudentRespVO b/yudao-module-infra/src/test/resources/codegen/windows10/vue2_one/java/InfraStudentRespVO index f86f679..5c15954 100644 --- a/yudao-module-infra/src/test/resources/codegen/windows10/vue2_one/java/InfraStudentRespVO +++ b/yudao-module-infra/src/test/resources/codegen/windows10/vue2_one/java/InfraStudentRespVO @@ -6,7 +6,8 @@ import java.util.*; import java.util.*; import org.springframework.format.annotation.DateTimeFormat; import java.time.LocalDateTime; -import cn.idev.excel.annotation.*; +import com.alibaba.excel.annotation.ExcelIgnoreUnannotated; +import com.alibaba.excel.annotation.ExcelProperty; import cn.iocoder.yudao.framework.excel.core.annotations.DictFormat; import cn.iocoder.yudao.framework.excel.core.convert.DictConvert; diff --git a/yudao-module-infra/src/test/resources/codegen/windows10/vue2_tree/java/InfraCategoryRespVO b/yudao-module-infra/src/test/resources/codegen/windows10/vue2_tree/java/InfraCategoryRespVO index 335881c..d144ce2 100644 --- a/yudao-module-infra/src/test/resources/codegen/windows10/vue2_tree/java/InfraCategoryRespVO +++ b/yudao-module-infra/src/test/resources/codegen/windows10/vue2_tree/java/InfraCategoryRespVO @@ -4,7 +4,8 @@ import io.swagger.v3.oas.annotations.media.Schema; import lombok.*; import java.util.*; import java.util.*; -import cn.idev.excel.annotation.*; +import com.alibaba.excel.annotation.ExcelIgnoreUnannotated; +import com.alibaba.excel.annotation.ExcelProperty; @Schema(description = "管理后台 - 分类 Response VO") @Data diff --git a/yudao-module-infra/src/test/resources/codegen/windows10/vue3_master_erp/java/InfraStudentRespVO b/yudao-module-infra/src/test/resources/codegen/windows10/vue3_master_erp/java/InfraStudentRespVO index f86f679..5c15954 100644 --- a/yudao-module-infra/src/test/resources/codegen/windows10/vue3_master_erp/java/InfraStudentRespVO +++ b/yudao-module-infra/src/test/resources/codegen/windows10/vue3_master_erp/java/InfraStudentRespVO @@ -6,7 +6,8 @@ import java.util.*; import java.util.*; import org.springframework.format.annotation.DateTimeFormat; import java.time.LocalDateTime; -import cn.idev.excel.annotation.*; +import com.alibaba.excel.annotation.ExcelIgnoreUnannotated; +import com.alibaba.excel.annotation.ExcelProperty; import cn.iocoder.yudao.framework.excel.core.annotations.DictFormat; import cn.iocoder.yudao.framework.excel.core.convert.DictConvert; diff --git a/yudao-module-infra/src/test/resources/codegen/windows10/vue3_master_inner/java/InfraStudentRespVO b/yudao-module-infra/src/test/resources/codegen/windows10/vue3_master_inner/java/InfraStudentRespVO index f86f679..5c15954 100644 --- a/yudao-module-infra/src/test/resources/codegen/windows10/vue3_master_inner/java/InfraStudentRespVO +++ b/yudao-module-infra/src/test/resources/codegen/windows10/vue3_master_inner/java/InfraStudentRespVO @@ -6,7 +6,8 @@ import java.util.*; import java.util.*; import org.springframework.format.annotation.DateTimeFormat; import java.time.LocalDateTime; -import cn.idev.excel.annotation.*; +import com.alibaba.excel.annotation.ExcelIgnoreUnannotated; +import com.alibaba.excel.annotation.ExcelProperty; import cn.iocoder.yudao.framework.excel.core.annotations.DictFormat; import cn.iocoder.yudao.framework.excel.core.convert.DictConvert; diff --git a/yudao-module-infra/src/test/resources/codegen/windows10/vue3_master_normal/java/InfraStudentRespVO b/yudao-module-infra/src/test/resources/codegen/windows10/vue3_master_normal/java/InfraStudentRespVO index f86f679..5c15954 100644 --- a/yudao-module-infra/src/test/resources/codegen/windows10/vue3_master_normal/java/InfraStudentRespVO +++ b/yudao-module-infra/src/test/resources/codegen/windows10/vue3_master_normal/java/InfraStudentRespVO @@ -6,7 +6,8 @@ import java.util.*; import java.util.*; import org.springframework.format.annotation.DateTimeFormat; import java.time.LocalDateTime; -import cn.idev.excel.annotation.*; +import com.alibaba.excel.annotation.ExcelIgnoreUnannotated; +import com.alibaba.excel.annotation.ExcelProperty; import cn.iocoder.yudao.framework.excel.core.annotations.DictFormat; import cn.iocoder.yudao.framework.excel.core.convert.DictConvert; diff --git a/yudao-module-infra/src/test/resources/codegen/windows10/vue3_one/java/InfraStudentRespVO b/yudao-module-infra/src/test/resources/codegen/windows10/vue3_one/java/InfraStudentRespVO index f86f679..5c15954 100644 --- a/yudao-module-infra/src/test/resources/codegen/windows10/vue3_one/java/InfraStudentRespVO +++ b/yudao-module-infra/src/test/resources/codegen/windows10/vue3_one/java/InfraStudentRespVO @@ -6,7 +6,8 @@ import java.util.*; import java.util.*; import org.springframework.format.annotation.DateTimeFormat; import java.time.LocalDateTime; -import cn.idev.excel.annotation.*; +import com.alibaba.excel.annotation.ExcelIgnoreUnannotated; +import com.alibaba.excel.annotation.ExcelProperty; import cn.iocoder.yudao.framework.excel.core.annotations.DictFormat; import cn.iocoder.yudao.framework.excel.core.convert.DictConvert; diff --git a/yudao-module-infra/src/test/resources/codegen/windows10/vue3_tree/java/InfraCategoryRespVO b/yudao-module-infra/src/test/resources/codegen/windows10/vue3_tree/java/InfraCategoryRespVO index 335881c..d144ce2 100644 --- a/yudao-module-infra/src/test/resources/codegen/windows10/vue3_tree/java/InfraCategoryRespVO +++ b/yudao-module-infra/src/test/resources/codegen/windows10/vue3_tree/java/InfraCategoryRespVO @@ -4,7 +4,8 @@ import io.swagger.v3.oas.annotations.media.Schema; import lombok.*; import java.util.*; import java.util.*; -import cn.idev.excel.annotation.*; +import com.alibaba.excel.annotation.ExcelIgnoreUnannotated; +import com.alibaba.excel.annotation.ExcelProperty; @Schema(description = "管理后台 - 分类 Response VO") @Data diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/api/device/IoTDeviceApiImpl.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/api/device/IoTDeviceApiImpl.java deleted file mode 100644 index 393d593..0000000 --- a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/api/device/IoTDeviceApiImpl.java +++ /dev/null @@ -1,142 +0,0 @@ -package cn.iocoder.yudao.module.iot.api.device; - -import cn.hutool.core.collection.CollUtil; -import cn.hutool.core.util.ObjUtil; -import cn.hutool.core.util.StrUtil; -import cn.iocoder.yudao.framework.common.enums.RpcConstants; -import cn.iocoder.yudao.framework.common.pojo.CommonResult; -import cn.iocoder.yudao.framework.common.util.object.BeanUtils; -import cn.iocoder.yudao.framework.tenant.core.aop.TenantIgnore; -import cn.iocoder.yudao.module.iot.core.biz.IotDeviceCommonApi; -import cn.iocoder.yudao.module.iot.core.biz.dto.*; -import cn.iocoder.yudao.module.iot.core.topic.auth.IotDeviceRegisterReqDTO; -import cn.iocoder.yudao.module.iot.core.topic.auth.IotDeviceRegisterRespDTO; -import cn.iocoder.yudao.module.iot.core.topic.auth.IotSubDeviceRegisterRespDTO; -import cn.iocoder.yudao.module.iot.dal.dataobject.device.IotDeviceDO; -import cn.iocoder.yudao.module.iot.dal.dataobject.device.IotDeviceModbusConfigDO; -import cn.iocoder.yudao.module.iot.dal.dataobject.device.IotDeviceModbusPointDO; -import cn.iocoder.yudao.module.iot.dal.dataobject.product.IotProductDO; -import cn.iocoder.yudao.module.iot.service.device.IotDeviceModbusConfigService; -import cn.iocoder.yudao.module.iot.service.device.IotDeviceModbusPointService; -import cn.iocoder.yudao.module.iot.service.device.IotDeviceService; -import cn.iocoder.yudao.module.iot.service.product.IotProductService; -import org.springframework.context.annotation.Lazy; -import org.springframework.context.annotation.Primary; -import org.springframework.validation.annotation.Validated; -import org.springframework.web.bind.annotation.PostMapping; -import org.springframework.web.bind.annotation.RequestBody; -import org.springframework.web.bind.annotation.RestController; - -import javax.annotation.Resource; -import javax.annotation.security.PermitAll; -import java.util.ArrayList; -import java.util.List; -import java.util.Map; -import java.util.Set; - -import static cn.iocoder.yudao.framework.common.pojo.CommonResult.success; -import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertSet; - -/** - * IoT 设备 API 实现类 - * - * @author haohao - */ -@RestController -@Validated -@Primary // 保证优先匹配,因为 yudao-iot-gateway 也有 IotDeviceCommonApi 的实现,并且也可能会被 biz 引入 -public class IoTDeviceApiImpl implements IotDeviceCommonApi { - - @Resource - private IotDeviceService deviceService; - @Resource - private IotProductService productService; - @Resource - @Lazy // 延迟加载,解决循环依赖 - private IotDeviceModbusConfigService modbusConfigService; - @Resource - @Lazy // 延迟加载,解决循环依赖 - private IotDeviceModbusPointService modbusPointService; - - @Override - @PostMapping(RpcConstants.RPC_API_PREFIX + "/iot/device/auth") - @PermitAll - public CommonResult authDevice(@RequestBody IotDeviceAuthReqDTO authReqDTO) { - return success(deviceService.authDevice(authReqDTO)); - } - - @Override - @PostMapping(RpcConstants.RPC_API_PREFIX + "/iot/device/get") // 特殊:方便调用,暂时使用 POST,实际更推荐 GET - @PermitAll - public CommonResult getDevice(@RequestBody IotDeviceGetReqDTO getReqDTO) { - IotDeviceDO device = getReqDTO.getId() != null ? deviceService.getDeviceFromCache(getReqDTO.getId()) - : deviceService.getDeviceFromCache(getReqDTO.getProductKey(), getReqDTO.getDeviceName()); - return success(BeanUtils.toBean(device, IotDeviceRespDTO.class, deviceDTO -> { - IotProductDO product = productService.getProductFromCache(deviceDTO.getProductId()); - if (product != null) { - deviceDTO.setProtocolType(product.getProtocolType()).setSerializeType(product.getSerializeType()); - } - })); - } - - @Override - @PostMapping(RpcConstants.RPC_API_PREFIX + "/iot/modbus/config-list") - @PermitAll - @TenantIgnore - public CommonResult> getModbusDeviceConfigList( - @RequestBody IotModbusDeviceConfigListReqDTO listReqDTO) { - // 1. 获取 Modbus 连接配置 - List configList = modbusConfigService.getDeviceModbusConfigList(listReqDTO); - if (CollUtil.isEmpty(configList)) { - return success(new ArrayList<>()); - } - - // 2. 组装返回结果 - Set deviceIds = convertSet(configList, IotDeviceModbusConfigDO::getDeviceId); - Map deviceMap = deviceService.getDeviceMap(deviceIds); - Map> pointMap = modbusPointService.getEnabledDeviceModbusPointMapByDeviceIds(deviceIds); - Map productMap = productService.getProductMap(convertSet(deviceMap.values(), IotDeviceDO::getProductId)); - List result = new ArrayList<>(configList.size()); - for (IotDeviceModbusConfigDO config : configList) { - // 3.1 获取设备信息 - IotDeviceDO device = deviceMap.get(config.getDeviceId()); - if (device == null) { - continue; - } - // 3.2 按 protocolType 筛选(如果非空) - if (StrUtil.isNotEmpty(listReqDTO.getProtocolType())) { - IotProductDO product = productMap.get(device.getProductId()); - if (product == null || ObjUtil.notEqual(listReqDTO.getProtocolType(), product.getProtocolType())) { - continue; - } - } - // 3.3 获取启用的点位列表 - List pointList = pointMap.get(config.getDeviceId()); - if (CollUtil.isEmpty(pointList)) { - continue; - } - - // 3.4 构建 IotModbusDeviceConfigRespDTO 对象 - IotModbusDeviceConfigRespDTO configDTO = BeanUtils.toBean(config, IotModbusDeviceConfigRespDTO.class, o -> - o.setProductKey(device.getProductKey()).setDeviceName(device.getDeviceName()) - .setPoints(BeanUtils.toBean(pointList, IotModbusPointRespDTO.class))); - result.add(configDTO); - } - return success(result); - } - - @Override - @PostMapping(RpcConstants.RPC_API_PREFIX + "/iot/device/register") - @PermitAll - public CommonResult registerDevice(@RequestBody IotDeviceRegisterReqDTO reqDTO) { - return success(deviceService.registerDevice(reqDTO)); - } - - @Override - @PostMapping(RpcConstants.RPC_API_PREFIX + "/iot/device/register-sub") - @PermitAll - public CommonResult> registerSubDevices(@RequestBody IotSubDeviceRegisterFullReqDTO reqDTO) { - return success(deviceService.registerSubDevices(reqDTO)); - } - -} \ No newline at end of file diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/api/package-info.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/api/package-info.java deleted file mode 100644 index 63bca16..0000000 --- a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/api/package-info.java +++ /dev/null @@ -1,4 +0,0 @@ -/** - * iot API 包,定义并实现提供给其它模块的 API - */ -package cn.iocoder.yudao.module.iot.api; \ No newline at end of file diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/alert/IotAlertConfigController.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/alert/IotAlertConfigController.java deleted file mode 100644 index b7f3cc1..0000000 --- a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/alert/IotAlertConfigController.java +++ /dev/null @@ -1,104 +0,0 @@ -package cn.iocoder.yudao.module.iot.controller.admin.alert; - -import cn.iocoder.yudao.framework.common.enums.CommonStatusEnum; -import cn.iocoder.yudao.framework.common.pojo.CommonResult; -import cn.iocoder.yudao.framework.common.pojo.PageResult; -import cn.iocoder.yudao.framework.common.util.object.BeanUtils; -import cn.iocoder.yudao.module.iot.controller.admin.alert.vo.config.IotAlertConfigPageReqVO; -import cn.iocoder.yudao.module.iot.controller.admin.alert.vo.config.IotAlertConfigRespVO; -import cn.iocoder.yudao.module.iot.controller.admin.alert.vo.config.IotAlertConfigSaveReqVO; -import cn.iocoder.yudao.module.iot.dal.dataobject.alert.IotAlertConfigDO; -import cn.iocoder.yudao.module.iot.service.alert.IotAlertConfigService; -import cn.iocoder.yudao.module.system.api.user.AdminUserApi; -import cn.iocoder.yudao.module.system.api.user.dto.AdminUserRespDTO; -import io.swagger.v3.oas.annotations.Operation; -import io.swagger.v3.oas.annotations.Parameter; -import io.swagger.v3.oas.annotations.tags.Tag; -import org.springframework.security.access.prepost.PreAuthorize; -import org.springframework.validation.annotation.Validated; -import org.springframework.web.bind.annotation.*; - -import javax.annotation.Resource; -import javax.validation.Valid; -import java.util.List; -import java.util.Map; -import java.util.Objects; -import java.util.stream.Collectors; - -import static cn.iocoder.yudao.framework.common.pojo.CommonResult.success; -import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertList; -import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertSetByFlatMap; - -@Tag(name = "管理后台 - IoT 告警配置") -@RestController -@RequestMapping("/iot/alert-config") -@Validated -public class IotAlertConfigController { - - @Resource - private IotAlertConfigService alertConfigService; - - @Resource - private AdminUserApi adminUserApi; - - @PostMapping("/create") - @Operation(summary = "创建告警配置") - @PreAuthorize("@ss.hasPermission('iot:alert-config:create')") - public CommonResult createAlertConfig(@Valid @RequestBody IotAlertConfigSaveReqVO createReqVO) { - return success(alertConfigService.createAlertConfig(createReqVO)); - } - - @PutMapping("/update") - @Operation(summary = "更新告警配置") - @PreAuthorize("@ss.hasPermission('iot:alert-config:update')") - public CommonResult updateAlertConfig(@Valid @RequestBody IotAlertConfigSaveReqVO updateReqVO) { - alertConfigService.updateAlertConfig(updateReqVO); - return success(true); - } - - @DeleteMapping("/delete") - @Operation(summary = "删除告警配置") - @Parameter(name = "id", description = "编号", required = true) - @PreAuthorize("@ss.hasPermission('iot:alert-config:delete')") - public CommonResult deleteAlertConfig(@RequestParam("id") Long id) { - alertConfigService.deleteAlertConfig(id); - return success(true); - } - - @GetMapping("/get") - @Operation(summary = "获得告警配置") - @Parameter(name = "id", description = "编号", required = true, example = "1024") - @PreAuthorize("@ss.hasPermission('iot:alert-config:query')") - public CommonResult getAlertConfig(@RequestParam("id") Long id) { - IotAlertConfigDO alertConfig = alertConfigService.getAlertConfig(id); - return success(BeanUtils.toBean(alertConfig, IotAlertConfigRespVO.class)); - } - - @GetMapping("/page") - @Operation(summary = "获得告警配置分页") - @PreAuthorize("@ss.hasPermission('iot:alert-config:query')") - public CommonResult> getAlertConfigPage(@Valid IotAlertConfigPageReqVO pageReqVO) { - PageResult pageResult = alertConfigService.getAlertConfigPage(pageReqVO); - - // 转换返回 - Map userMap = adminUserApi.getUserMap( - convertSetByFlatMap(pageResult.getList(), config -> config.getReceiveUserIds().stream())); - return success(BeanUtils.toBean(pageResult, IotAlertConfigRespVO.class, vo -> { - vo.setReceiveUserNames(vo.getReceiveUserIds().stream() - .map(userMap::get) - .filter(Objects::nonNull) - .map(AdminUserRespDTO::getNickname) - .collect(Collectors.toList())); - })); - } - - @GetMapping("/simple-list") - @Operation(summary = "获得告警配置简单列表", description = "只包含被开启的告警配置,主要用于前端的下拉选项") - @PreAuthorize("@ss.hasPermission('iot:alert-config:query')") - public CommonResult> getAlertConfigSimpleList() { - List list = alertConfigService.getAlertConfigListByStatus(CommonStatusEnum.ENABLE.getStatus()); - return success(convertList(list, config -> // 只返回 id、name 字段 - new IotAlertConfigRespVO().setId(config.getId()).setName(config.getName()))); - } - -} \ No newline at end of file diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/alert/IotAlertRecordController.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/alert/IotAlertRecordController.java deleted file mode 100644 index 7a586fd..0000000 --- a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/alert/IotAlertRecordController.java +++ /dev/null @@ -1,58 +0,0 @@ -package cn.iocoder.yudao.module.iot.controller.admin.alert; - -import cn.iocoder.yudao.framework.common.pojo.CommonResult; -import cn.iocoder.yudao.framework.common.pojo.PageResult; -import cn.iocoder.yudao.framework.common.util.object.BeanUtils; -import cn.iocoder.yudao.module.iot.controller.admin.alert.vo.recrod.IotAlertRecordPageReqVO; -import cn.iocoder.yudao.module.iot.controller.admin.alert.vo.recrod.IotAlertRecordProcessReqVO; -import cn.iocoder.yudao.module.iot.controller.admin.alert.vo.recrod.IotAlertRecordRespVO; -import cn.iocoder.yudao.module.iot.dal.dataobject.alert.IotAlertRecordDO; -import cn.iocoder.yudao.module.iot.service.alert.IotAlertRecordService; -import io.swagger.v3.oas.annotations.Operation; -import io.swagger.v3.oas.annotations.Parameter; -import io.swagger.v3.oas.annotations.tags.Tag; -import org.springframework.security.access.prepost.PreAuthorize; -import org.springframework.validation.annotation.Validated; -import org.springframework.web.bind.annotation.*; - -import javax.annotation.Resource; -import javax.validation.Valid; - -import static cn.iocoder.yudao.framework.common.pojo.CommonResult.success; -import static java.util.Collections.singleton; - -@Tag(name = "管理后台 - IoT 告警记录") -@RestController -@RequestMapping("/iot/alert-record") -@Validated -public class IotAlertRecordController { - - @Resource - private IotAlertRecordService alertRecordService; - - @GetMapping("/get") - @Operation(summary = "获得告警记录") - @Parameter(name = "id", description = "编号", required = true, example = "1024") - @PreAuthorize("@ss.hasPermission('iot:alert-record:query')") - public CommonResult getAlertRecord(@RequestParam("id") Long id) { - IotAlertRecordDO alertRecord = alertRecordService.getAlertRecord(id); - return success(BeanUtils.toBean(alertRecord, IotAlertRecordRespVO.class)); - } - - @GetMapping("/page") - @Operation(summary = "获得告警记录分页") - @PreAuthorize("@ss.hasPermission('iot:alert-record:query')") - public CommonResult> getAlertRecordPage(@Valid IotAlertRecordPageReqVO pageReqVO) { - PageResult pageResult = alertRecordService.getAlertRecordPage(pageReqVO); - return success(BeanUtils.toBean(pageResult, IotAlertRecordRespVO.class)); - } - - @PutMapping("/process") - @Operation(summary = "处理告警记录") - @PreAuthorize("@ss.hasPermission('iot:alert-record:process')") - public CommonResult processAlertRecord(@Valid @RequestBody IotAlertRecordProcessReqVO processReqVO) { - alertRecordService.processAlertRecordList(singleton(processReqVO.getId()), processReqVO.getProcessRemark()); - return success(true); - } - -} \ No newline at end of file diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/alert/vo/config/IotAlertConfigPageReqVO.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/alert/vo/config/IotAlertConfigPageReqVO.java deleted file mode 100644 index 0f9a1e9..0000000 --- a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/alert/vo/config/IotAlertConfigPageReqVO.java +++ /dev/null @@ -1,26 +0,0 @@ -package cn.iocoder.yudao.module.iot.controller.admin.alert.vo.config; - -import cn.iocoder.yudao.framework.common.pojo.PageParam; -import io.swagger.v3.oas.annotations.media.Schema; -import lombok.Data; -import org.springframework.format.annotation.DateTimeFormat; - -import java.time.LocalDateTime; - -import static cn.iocoder.yudao.framework.common.util.date.DateUtils.FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND; - -@Schema(description = "管理后台 - IoT 告警配置分页 Request VO") -@Data -public class IotAlertConfigPageReqVO extends PageParam { - - @Schema(description = "配置名称", example = "赵六") - private String name; - - @Schema(description = "配置状态", example = "1") - private Integer status; - - @Schema(description = "创建时间") - @DateTimeFormat(pattern = FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND) - private LocalDateTime[] createTime; - -} \ No newline at end of file diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/alert/vo/config/IotAlertConfigRespVO.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/alert/vo/config/IotAlertConfigRespVO.java deleted file mode 100644 index e68a7b7..0000000 --- a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/alert/vo/config/IotAlertConfigRespVO.java +++ /dev/null @@ -1,43 +0,0 @@ -package cn.iocoder.yudao.module.iot.controller.admin.alert.vo.config; - -import io.swagger.v3.oas.annotations.media.Schema; -import lombok.Data; - -import java.time.LocalDateTime; -import java.util.List; - -@Schema(description = "管理后台 - IoT 告警配置 Response VO") -@Data -public class IotAlertConfigRespVO { - - @Schema(description = "配置编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "3566") - private Long id; - - @Schema(description = "配置名称", requiredMode = Schema.RequiredMode.REQUIRED, example = "赵六") - private String name; - - @Schema(description = "配置描述", example = "你猜") - private String description; - - @Schema(description = "告警级别", requiredMode = Schema.RequiredMode.REQUIRED, example = "1") - private Integer level; - - @Schema(description = "配置状态", requiredMode = Schema.RequiredMode.REQUIRED, example = "1") - private Integer status; - - @Schema(description = "关联的场景联动规则编号数组", requiredMode = Schema.RequiredMode.REQUIRED, example = "1,2,3") - private List sceneRuleIds; - - @Schema(description = "接收的用户编号数组", requiredMode = Schema.RequiredMode.REQUIRED, example = "100,200") - private List receiveUserIds; - - @Schema(description = "接收的用户名称数组", requiredMode = Schema.RequiredMode.REQUIRED, example = "张三,李四") - private List receiveUserNames; - - @Schema(description = "接收的类型数组", requiredMode = Schema.RequiredMode.REQUIRED, example = "1,2,3") - private List receiveTypes; - - @Schema(description = "创建时间", requiredMode = Schema.RequiredMode.REQUIRED) - private LocalDateTime createTime; - -} \ No newline at end of file diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/alert/vo/config/IotAlertConfigSaveReqVO.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/alert/vo/config/IotAlertConfigSaveReqVO.java deleted file mode 100644 index 37b99da..0000000 --- a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/alert/vo/config/IotAlertConfigSaveReqVO.java +++ /dev/null @@ -1,47 +0,0 @@ -package cn.iocoder.yudao.module.iot.controller.admin.alert.vo.config; - -import cn.iocoder.yudao.framework.common.enums.CommonStatusEnum; -import cn.iocoder.yudao.framework.common.validation.InEnum; -import io.swagger.v3.oas.annotations.media.Schema; -import lombok.Data; - -import javax.validation.constraints.NotEmpty; -import javax.validation.constraints.NotNull; -import java.util.List; - -@Schema(description = "管理后台 - IoT 告警配置新增/修改 Request VO") -@Data -public class IotAlertConfigSaveReqVO { - - @Schema(description = "配置编号", example = "3566") - private Long id; - - @Schema(description = "配置名称", requiredMode = Schema.RequiredMode.REQUIRED, example = "赵六") - @NotEmpty(message = "配置名称不能为空") - private String name; - - @Schema(description = "配置描述", example = "你猜") - private String description; - - @Schema(description = "告警级别", requiredMode = Schema.RequiredMode.REQUIRED, example = "1") - @NotNull(message = "告警级别不能为空") - private Integer level; - - @Schema(description = "配置状态", requiredMode = Schema.RequiredMode.REQUIRED, example = "1") - @NotNull(message = "配置状态不能为空") - @InEnum(CommonStatusEnum.class) - private Integer status; - - @Schema(description = "关联的场景联动规则编号数组") - @NotEmpty(message = "关联的场景联动规则编号数组不能为空") - private List sceneRuleIds; - - @Schema(description = "接收的用户编号数组") - @NotEmpty(message = "接收的用户编号数组不能为空") - private List receiveUserIds; - - @Schema(description = "接收的类型数组") - @NotEmpty(message = "接收的类型数组不能为空") - private List receiveTypes; - -} \ No newline at end of file diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/alert/vo/recrod/IotAlertRecordPageReqVO.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/alert/vo/recrod/IotAlertRecordPageReqVO.java deleted file mode 100644 index 109f240..0000000 --- a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/alert/vo/recrod/IotAlertRecordPageReqVO.java +++ /dev/null @@ -1,35 +0,0 @@ -package cn.iocoder.yudao.module.iot.controller.admin.alert.vo.recrod; - -import cn.iocoder.yudao.framework.common.pojo.PageParam; -import io.swagger.v3.oas.annotations.media.Schema; -import lombok.Data; -import org.springframework.format.annotation.DateTimeFormat; - -import java.time.LocalDateTime; - -import static cn.iocoder.yudao.framework.common.util.date.DateUtils.FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND; - -@Schema(description = "管理后台 - IoT 告警记录分页 Request VO") -@Data -public class IotAlertRecordPageReqVO extends PageParam { - - @Schema(description = "告警配置编号", example = "29320") - private Long configId; - - @Schema(description = "告警级别", example = "1") - private Integer level; - - @Schema(description = "产品编号", example = "2050") - private Long productId; - - @Schema(description = "设备编号", example = "21727") - private String deviceId; - - @Schema(description = "是否处理", example = "true") - private Boolean processStatus; - - @Schema(description = "创建时间") - @DateTimeFormat(pattern = FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND) - private LocalDateTime[] createTime; - -} \ No newline at end of file diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/alert/vo/recrod/IotAlertRecordProcessReqVO.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/alert/vo/recrod/IotAlertRecordProcessReqVO.java deleted file mode 100644 index e967150..0000000 --- a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/alert/vo/recrod/IotAlertRecordProcessReqVO.java +++ /dev/null @@ -1,19 +0,0 @@ -package cn.iocoder.yudao.module.iot.controller.admin.alert.vo.recrod; - -import io.swagger.v3.oas.annotations.media.Schema; -import lombok.Data; - -import javax.validation.constraints.NotNull; - -@Schema(description = "管理后台 - IoT 告警记录处理 Request VO") -@Data -public class IotAlertRecordProcessReqVO { - - @Schema(description = "记录编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "1024") - @NotNull(message = "记录编号不能为空") - private Long id; - - @Schema(description = "处理结果(备注)", requiredMode = Schema.RequiredMode.REQUIRED, example = "已处理告警,问题已解决") - private String processRemark; - -} \ No newline at end of file diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/alert/vo/recrod/IotAlertRecordRespVO.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/alert/vo/recrod/IotAlertRecordRespVO.java deleted file mode 100644 index 97ccf6c..0000000 --- a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/alert/vo/recrod/IotAlertRecordRespVO.java +++ /dev/null @@ -1,43 +0,0 @@ -package cn.iocoder.yudao.module.iot.controller.admin.alert.vo.recrod; - -import cn.iocoder.yudao.module.iot.core.mq.message.IotDeviceMessage; -import io.swagger.v3.oas.annotations.media.Schema; -import lombok.Data; - -import java.time.LocalDateTime; - -@Schema(description = "管理后台 - IoT 告警记录 Response VO") -@Data -public class IotAlertRecordRespVO { - - @Schema(description = "记录编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "19904") - private Long id; - - @Schema(description = "告警配置编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "29320") - private Long configId; - - @Schema(description = "告警名称", requiredMode = Schema.RequiredMode.REQUIRED, example = "张三") - private String configName; - - @Schema(description = "告警级别", requiredMode = Schema.RequiredMode.REQUIRED, example = "1") - private Integer configLevel; - - @Schema(description = "产品编号", example = "2050") - private Long productId; - - @Schema(description = "设备编号", example = "21727") - private Long deviceId; - - @Schema(description = "触发的设备消息") - private IotDeviceMessage deviceMessage; - - @Schema(description = "是否处理", requiredMode = Schema.RequiredMode.REQUIRED, example = "1") - private Boolean processStatus; - - @Schema(description = "处理结果(备注)", example = "你说的对") - private String processRemark; - - @Schema(description = "创建时间", requiredMode = Schema.RequiredMode.REQUIRED) - private LocalDateTime createTime; - -} \ No newline at end of file diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/device/DeviceController.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/device/DeviceController.java new file mode 100644 index 0000000..c4e57f7 --- /dev/null +++ b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/device/DeviceController.java @@ -0,0 +1,353 @@ +package cn.iocoder.yudao.module.iot.controller.admin.device; + +import cn.iocoder.yudao.framework.apilog.core.annotation.ApiAccessLog; +import cn.iocoder.yudao.framework.common.pojo.CommonResult; +import cn.iocoder.yudao.framework.common.pojo.PageParam; +import cn.iocoder.yudao.framework.common.pojo.PageResult; +import cn.iocoder.yudao.framework.excel.core.util.ExcelUtils; +import cn.iocoder.yudao.module.infra.service.job.JobService; +import cn.iocoder.yudao.module.iot.controller.admin.device.vo.*; +//import cn.iocoder.yudao.module.iot.controller.admin.devicecontactmodel.vo.DeviceContactModelPageReqVO; +import cn.iocoder.yudao.module.iot.dal.dataobject.device.DeviceAttributeDO; +import cn.iocoder.yudao.module.iot.dal.dataobject.device.DeviceDO; +//import cn.iocoder.yudao.module.iot.dal.dataobject.devicecontactmodel.DeviceContactModelDO; +import cn.iocoder.yudao.module.iot.dal.dataobject.devicecontactmodel.DeviceContactModelDO; +import cn.iocoder.yudao.module.iot.service.device.DeviceService; +import cn.iocoder.yudao.module.iot.service.device.TDengineService; +import com.fasterxml.jackson.core.JsonProcessingException; +import io.swagger.v3.oas.annotations.Operation; +import io.swagger.v3.oas.annotations.Parameter; +import io.swagger.v3.oas.annotations.tags.Tag; +import org.eclipse.paho.client.mqttv3.MqttException; +import org.quartz.SchedulerException; +import org.springframework.security.access.prepost.PreAuthorize; +import org.springframework.validation.annotation.Validated; +import org.springframework.web.bind.annotation.*; + +import javax.annotation.Resource; +import javax.servlet.http.HttpServletResponse; +import javax.validation.Valid; +import java.io.IOException; +import java.time.LocalDateTime; +import java.time.format.DateTimeFormatter; +import java.util.Arrays; +import java.util.List; +import java.util.Map; +import java.util.stream.Collectors; + +import static cn.iocoder.yudao.framework.apilog.core.enums.OperateTypeEnum.EXPORT; +import static cn.iocoder.yudao.framework.common.pojo.CommonResult.success; + +@Tag(name = "管理后台 - 物联设备") +@RestController +@RequestMapping("/iot/device") +@Validated +public class DeviceController { + + @Resource + private DeviceService deviceService; + @Resource + private TDengineService tDengineService; + @Resource + private JobService jobService; + +// @Resource +// private OpcUaSubscriptionService opcUaService; + +// @Resource +// private IMqttservice mqttService; + + + @PostMapping("/create") + @Operation(summary = "创建物联设备") + @PreAuthorize("@ss.hasPermission('iot:device:create')") + public CommonResult createDevice(@Valid @RequestBody DeviceSaveReqVO createReqVO) throws SchedulerException { + DeviceDO device = deviceService.createDevice(createReqVO); + //初始化Td表 +// tDengineService.initDatabaseAndTable(device.getId()); + + return success(device); + } + + @PutMapping("/update") + @Operation(summary = "更新物联设备") + @PreAuthorize("@ss.hasPermission('iot:device:update')") + public CommonResult updateDevice(@Valid @RequestBody DeviceSaveReqVO updateReqVO) { + deviceService.updateDevice(updateReqVO); + return success(true); + } + + @DeleteMapping("/delete") + @Operation(summary = "删除物联设备") + @Parameter(name = "ids", description = "编号", required = true) + @PreAuthorize("@ss.hasPermission('iot:device:delete')") + public CommonResult deleteDevice(@RequestParam("ids") String ids) { + // 将逗号分隔的字符串转换为Long类型的List + List idList = Arrays.stream(ids.split(",")) + .map(String::trim) // 去除可能存在的空格 + .map(Long::valueOf) + .collect(Collectors.toList()); + + deviceService.deleteDevice(idList); + return success(true); + } + + @GetMapping("/get") + @Operation(summary = "获得物联设备") + @Parameter(name = "id", description = "编号", required = true, example = "1024") + @PreAuthorize("@ss.hasPermission('iot:device:query')") + public CommonResult getDevice(@RequestParam("id") Long id) { + DeviceRespVO deviceRespVO = deviceService.getDevice(id); + return success(deviceRespVO); + } + + @GetMapping("/page") + @Operation(summary = "获得物联设备分页") + @PreAuthorize("@ss.hasPermission('iot:device:query')") + public CommonResult> getDevicePage(@Valid DevicePageReqVO pageReqVO) { + PageResult pageResult = deviceService.getDevicePage(pageReqVO); + return success(pageResult); + } + + @GetMapping("/export-excel") + @Operation(summary = "导出物联设备 Excel") + @PreAuthorize("@ss.hasPermission('iot:device:export')") + @ApiAccessLog(operateType = EXPORT) + public void exportDeviceExcel(@Valid DevicePageReqVO pageReqVO, + HttpServletResponse response) throws IOException { + List list = deviceService.getDevicePage(pageReqVO).getList(); + // 导出 Excel + ExcelUtils.write(response, "物联设备.xls", "数据", DeviceRespVO.class,list); + } + @GetMapping("/deviceList") +// @PreAuthorize("@ss.hasPermission('iot:device:query')") + public CommonResult> deviceList(@Valid DevicePageReqVO pageReqVO) { + pageReqVO.setPageSize(PageParam.PAGE_SIZE_NONE); + List list = deviceService.getDevicePage(pageReqVO).getList(); + return success(list); + } + + @GetMapping("/noUsedlist") + @Operation(summary = "获得未关联设备台账列表") + @PreAuthorize("@ss.hasPermission('mes:device-ledger:query')") + public CommonResult> getDeviceLedgerListByNoUsed() { + DevicePageReqVO pageReqVO = new DevicePageReqVO(); + pageReqVO.setPageSize(PageParam.PAGE_SIZE_NONE); + List list = deviceService.getDevicePage(pageReqVO).getList(); + List ids = deviceService.deviceLedgerList(); + List filteredList = list.stream() + .filter(device -> !ids.contains(device.getId())) + .collect(Collectors.toList()); + return success(filteredList); + } + + @GetMapping("/noUsedlist2") + @Operation(summary = "获得未关联设备台账列表") + @PreAuthorize("@ss.hasPermission('mes:device-ledger:query')") + public CommonResult> getDeviceLedgerList2ByNoUsed(@RequestParam("id") Long id) { + DevicePageReqVO pageReqVO = new DevicePageReqVO(); + pageReqVO.setPageSize(PageParam.PAGE_SIZE_NONE); + List list = deviceService.getDevicePage(pageReqVO).getList(); + List ids = deviceService.deviceLedgerList(); + + List filteredList = list.stream() + .filter(device -> { + if (id != null && id.equals(device.getId())) { + return true; + } + return !ids.contains(device.getId()); + }) + .collect(Collectors.toList()); + return success(filteredList); + } + + @PostMapping("/connect") + @Operation(summary = "连接") +// @PreAuthorize("@ss.hasPermission('iot:device:create')") + public CommonResult connectDevice(@RequestBody DeviceSaveReqVO createReqVO) throws SchedulerException { + + deviceService.connectDevice(createReqVO); + + //开启或停止定时任务 +// JobDO jobDO = jobService.getJobByDeviceId(createReqVO.getId()); +// jobService.updateJobStatus(jobDO.getId(), createReqVO.getIsConnect()); + + return success(Boolean.TRUE); + } + + + @PostMapping("/copy") + @Operation(summary = "复制设备") + @PreAuthorize("@ss.hasPermission('iot:device-model:update')") + public CommonResult copyDevice(@RequestParam("id") Long id) { + return success(deviceService.copyDevice(id)); + } + + + @GetMapping("/lineDevicePage") + @Operation(summary = "获得产线设备分页") +// @PreAuthorize("@ss.hasPermission('iot:device:lineDevicePage')") + public CommonResult> lineDevicePage(@Valid LineDeviceRequestVO pageReqVO) { + PageResult pageResult = deviceService.lineDevicePage(pageReqVO); + return success(pageResult); + } + + @GetMapping("/export-line-device") + @Operation(summary = "导出产线设备分页 Excel") +// @PreAuthorize("@ss.hasPermission('iot:device:lineDeviceExport')") + @ApiAccessLog(operateType = EXPORT) + public void exportLineDevice(@Valid LineDeviceRequestVO pageReqVO, + HttpServletResponse response) throws IOException { + pageReqVO.setPageSize(PageParam.PAGE_SIZE_NONE); + + + PageResult lineDeviceRespVOPageResult = deviceService.lineDevicePage(pageReqVO); + // 设置响应头 +// response.setContentType("application/vnd.ms-excel;charset=UTF-8"); +// response.setHeader("Content-Disposition", +// "attachment;filename=" + URLEncoder.encode("设备运行报表记录.xls", "UTF-8")); +// response.setHeader("Content-Encoding", "identity"); + // 导出Excel + String fileName = String.format("数据实时监控_%s.xls", LocalDateTime.now().format(DateTimeFormatter.ofPattern("yyyyMMddHHmmss"))); + + // 导出 Excel + ExcelUtils.write(response, fileName, "数据", LineDeviceRespVO.class,lineDeviceRespVOPageResult.getList()); + } + + + @GetMapping("/singleDevice") + @Operation(summary = "单设备查看") +// @PreAuthorize("@ss.hasPermission('iot:device:query')") + public CommonResult>>> singleDevice(@RequestParam("deviceId") Long deviceId) throws JsonProcessingException { + Map>> deviceContactModelDO=deviceService.singleDevice(deviceId); + return success(deviceContactModelDO); + } + + + @GetMapping("/historyRecord") + @Operation(summary = "历史记录查询") +// @PreAuthorize("@ss.hasPermission('iot:device:query')") + public CommonResult>> historyRecord(@RequestParam("deviceId") Long deviceId, + @RequestParam(name = "collectionStartTime", required = false) String collectionStartTime, + @RequestParam(name = "collectionEndTime", required = false) String collectionEndTime, + @RequestParam(name = "attributeCodes", required = false)List attributeCodes, + @RequestParam(name = "pageNo", required = false, defaultValue = "1") Integer page, + @RequestParam(name = "pageSize", required = false, defaultValue = "10") Integer pageSize + ) throws JsonProcessingException { + PageResult> deviceContactModelDO=deviceService.historyRecord(deviceId,collectionStartTime,collectionEndTime,attributeCodes,page,pageSize); + return success(deviceContactModelDO); + } + + + @GetMapping("/getDeviceOperationalStatus") + @Operation(summary = "获取首页设备运行状态") +// @PreAuthorize("@ss.hasPermission('iot:device:query')") + @Parameter(name = "orgId", description = "产线组织Id") + public CommonResult getDeviceOperationalStatus(@RequestParam(name = "orgId",required = false) Long orgId) throws JsonProcessingException { + DeviceOperationStatusRespVO deviceOperationalStatus=deviceService.getDeviceOperationalStatus(); + return success(deviceOperationalStatus); + } + + + // ==================== 子表(设备属性) ==================== + + @GetMapping("/device-attribute/page") + @Operation(summary = "获得设备属性分页") + @Parameter(name = "deviceId", description = "设备id") + @PreAuthorize("@ss.hasPermission('iot:device:query')") + public CommonResult> getDeviceAttributePage(PageParam pageParam, DeviceContactModelPageReqVO deviceModelAttributePageReqVO) { + return success(deviceService.getDeviceAttributePage(pageParam, deviceModelAttributePageReqVO)); + } + + + @GetMapping("/device-attribute/list") + @Operation(summary = "获得设备属性列表") + @Parameter(name = "deviceId", description = "设备id") + @PreAuthorize("@ss.hasPermission('iot:device:query')") + public CommonResult> getDeviceAttributeList(@RequestParam(name = "deviceId") Long deviceId) { + return success(deviceService.getDeviceAttributeList(deviceId)); + } + + + @PostMapping("/device-attribute/create") + @Operation(summary = "创建设备属性") + @PreAuthorize("@ss.hasPermission('iot:device:create')") + public CommonResult createDeviceAttribute(@Valid @RequestBody DeviceAttributeDO deviceAttribute) { + return success(deviceService.createDeviceAttribute(deviceAttribute)); + } + + @PutMapping("/device-attribute/update") + @Operation(summary = "更新设备属性") + @PreAuthorize("@ss.hasPermission('iot:device:update')") + public CommonResult updateDeviceAttribute(@Valid @RequestBody DeviceAttributeDO deviceAttribute) { + deviceService.updateDeviceAttribute(deviceAttribute); + return success(true); + } + + @DeleteMapping("/device-attribute/delete") + @Parameter(name = "id", description = "编号", required = true) + @Operation(summary = "删除设备属性") + @PreAuthorize("@ss.hasPermission('iot:device:delete')") + public CommonResult deleteDeviceAttribute(@RequestParam("id") Long id) { + deviceService.deleteDeviceAttribute(id); + return success(true); + } + + @GetMapping("/device-attribute/get") + @Operation(summary = "获得设备属性") + @Parameter(name = "id", description = "编号", required = true) + @PreAuthorize("@ss.hasPermission('iot:device:query')") + public CommonResult getDeviceAttribute(@RequestParam("id") Long id) { + return success(deviceService.getDeviceAttribute(id)); + } + + @GetMapping("/devicePointList") + @PreAuthorize("@ss.hasPermission('iot:device:query')") + public CommonResult> devicePointList() { + return success( deviceService.devicePointList()); + } + + + @PostMapping("/scheduledStart") + public CommonResult scheduledStart(@RequestParam("id") Long id) { + return success(deviceService.scheduledStart(id)); + } + + + @PostMapping("/scheduledStop") + public CommonResult scheduledStop(@RequestParam("id") Long id) { + return success(deviceService.scheduledStop(id)); + } + + @GetMapping("/device-attribute/batchList") + @Operation(summary = "获得多个设备的属性数据") + @Parameter(name = "goviewId", description = "大屏ID", required = true) + @PreAuthorize("@ss.hasPermission('iot:device:query')") + public CommonResult>> getMultiDeviceAttributes(@RequestParam("goviewId") Long goviewId) { + return success(deviceService.getMultiDeviceAttributes(goviewId)); + } + + +// @PostMapping("/subscribe") +// public String subscribeTopic(@RequestParam String topic) { +// try { +// int result = mqttService.subscribeTopic(topic); // 使用服务层安全订阅 +// if (result >0 ) { +// return "订阅成功: " + topic; +// } else { +// return "订阅失败: " + topic; +// } +// } catch (MqttException e) { +// return "订阅异常: " + e.getMessage(); +// } +// } + + @PutMapping("/update-enabled") + @Operation(summary = "更新任务管理启用状态") + @PreAuthorize("@ss.hasPermission('mes:task-management:update')") + public CommonResult updateDeviceEnabled(@Valid @RequestBody DeviceUpdateEnabledReqVO updateEnabledReqVO) throws MqttException { + deviceService.updateDeviceEnabled(updateEnabledReqVO); + return success(true); + } + +} \ No newline at end of file diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/device/IotDeviceController.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/device/IotDeviceController.java deleted file mode 100644 index b4f1850..0000000 --- a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/device/IotDeviceController.java +++ /dev/null @@ -1,254 +0,0 @@ -package cn.iocoder.yudao.module.iot.controller.admin.device; - -import cn.hutool.core.collection.CollUtil; -import cn.iocoder.yudao.framework.apilog.core.annotation.ApiAccessLog; -import cn.iocoder.yudao.framework.common.pojo.CommonResult; -import cn.iocoder.yudao.framework.common.pojo.PageParam; -import cn.iocoder.yudao.framework.common.pojo.PageResult; -import cn.iocoder.yudao.framework.common.util.collection.MapUtils; -import cn.iocoder.yudao.framework.common.util.object.BeanUtils; -import cn.iocoder.yudao.framework.excel.core.util.ExcelUtils; -import cn.iocoder.yudao.module.iot.controller.admin.device.vo.device.*; -import cn.iocoder.yudao.module.iot.dal.dataobject.device.IotDeviceDO; -import cn.iocoder.yudao.module.iot.dal.dataobject.product.IotProductDO; -import cn.iocoder.yudao.module.iot.service.device.IotDeviceService; -import cn.iocoder.yudao.module.iot.service.product.IotProductService; -import io.swagger.v3.oas.annotations.Operation; -import io.swagger.v3.oas.annotations.Parameter; -import io.swagger.v3.oas.annotations.Parameters; -import io.swagger.v3.oas.annotations.tags.Tag; -import org.springframework.security.access.prepost.PreAuthorize; -import org.springframework.validation.annotation.Validated; -import org.springframework.web.bind.annotation.*; -import org.springframework.web.multipart.MultipartFile; - -import javax.annotation.Resource; -import javax.servlet.http.HttpServletResponse; -import javax.validation.Valid; -import java.io.IOException; -import java.util.*; - -import static cn.iocoder.yudao.framework.apilog.core.enums.OperateTypeEnum.EXPORT; -import static cn.iocoder.yudao.framework.common.pojo.CommonResult.success; -import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertList; -import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertMap; - -@Tag(name = "管理后台 - IoT 设备") -@RestController -@RequestMapping("/iot/device") -@Validated -public class IotDeviceController { - - @Resource - private IotDeviceService deviceService; - @Resource - private IotProductService productService; - - @PostMapping("/create") - @Operation(summary = "创建设备") - @PreAuthorize("@ss.hasPermission('iot:device:create')") - public CommonResult createDevice(@Valid @RequestBody IotDeviceSaveReqVO createReqVO) { - return success(deviceService.createDevice(createReqVO)); - } - - - @PutMapping("/update") - @Operation(summary = "更新设备") - @PreAuthorize("@ss.hasPermission('iot:device:update')") - public CommonResult updateDevice(@Valid @RequestBody IotDeviceSaveReqVO updateReqVO) { - deviceService.updateDevice(updateReqVO); - return success(true); - } - - @PutMapping("/bind-gateway") - @Operation(summary = "绑定子设备到网关") - @PreAuthorize("@ss.hasPermission('iot:device:update')") - public CommonResult bindDeviceGateway(@Valid @RequestBody IotDeviceBindGatewayReqVO reqVO) { - deviceService.bindDeviceGateway(reqVO.getSubIds(), reqVO.getGatewayId()); - return success(true); - } - - @PutMapping("/unbind-gateway") - @Operation(summary = "解绑子设备与网关") - @PreAuthorize("@ss.hasPermission('iot:device:update')") - public CommonResult unbindDeviceGateway(@Valid @RequestBody IotDeviceUnbindGatewayReqVO reqVO) { - deviceService.unbindDeviceGateway(reqVO.getSubIds(), reqVO.getGatewayId()); - return success(true); - } - - @GetMapping("/sub-device-list") - @Operation(summary = "获取网关的子设备列表") - @Parameter(name = "gatewayId", description = "网关设备编号", required = true, example = "1") - @PreAuthorize("@ss.hasPermission('iot:device:query')") - public CommonResult> getSubDeviceList(@RequestParam("gatewayId") Long gatewayId) { - List list = deviceService.getDeviceListByGatewayId(gatewayId); - if (CollUtil.isEmpty(list)) { - return success(Collections.emptyList()); - } - - // 补充产品名称 - Map productMap = convertMap(productService.getProductList(), IotProductDO::getId); - return success(convertList(list, device -> { - IotDeviceRespVO respVO = BeanUtils.toBean(device, IotDeviceRespVO.class); - MapUtils.findAndThen(productMap, device.getProductId(), - product -> respVO.setProductName(product.getName())); - return respVO; - })); - } - - @GetMapping("/unbound-sub-device-page") - @Operation(summary = "获取未绑定网关的子设备分页") - @PreAuthorize("@ss.hasPermission('iot:device:query')") - public CommonResult> getUnboundSubDevicePage(@Valid IotDevicePageReqVO pageReqVO) { - PageResult pageResult = deviceService.getUnboundSubDevicePage(pageReqVO); - if (CollUtil.isEmpty(pageResult.getList())) { - return success(PageResult.empty()); - } - - // 补充产品名称 - Map productMap = convertMap(productService.getProductList(), IotProductDO::getId); - PageResult result = BeanUtils.toBean(pageResult, IotDeviceRespVO.class, device -> - MapUtils.findAndThen(productMap, device.getProductId(), product -> device.setProductName(product.getName()))); - return success(result); - } - - @PutMapping("/update-group") - @Operation(summary = "更新设备分组") - @PreAuthorize("@ss.hasPermission('iot:device:update')") - public CommonResult updateDeviceGroup(@Valid @RequestBody IotDeviceUpdateGroupReqVO updateReqVO) { - deviceService.updateDeviceGroup(updateReqVO); - return success(true); - } - - @DeleteMapping("/delete") - @Operation(summary = "删除单个设备") - @Parameter(name = "id", description = "编号", required = true) - @PreAuthorize("@ss.hasPermission('iot:device:delete')") - public CommonResult deleteDevice(@RequestParam("id") Long id) { - deviceService.deleteDevice(id); - return success(true); - } - - @DeleteMapping("/delete-list") - @Operation(summary = "删除多个设备") - @Parameter(name = "ids", description = "编号数组", required = true) - @PreAuthorize("@ss.hasPermission('iot:device:delete')") - public CommonResult deleteDeviceList(@RequestParam("ids") Collection ids) { - deviceService.deleteDeviceList(ids); - return success(true); - } - - @GetMapping("/get") - @Operation(summary = "获得设备") - @Parameter(name = "id", description = "编号", required = true, example = "1024") - @PreAuthorize("@ss.hasPermission('iot:device:query')") - public CommonResult getDevice(@RequestParam("id") Long id) { - IotDeviceDO device = deviceService.getDevice(id); - return success(BeanUtils.toBean(device, IotDeviceRespVO.class)); - } - - @GetMapping("/page") - @Operation(summary = "获得设备分页") - @PreAuthorize("@ss.hasPermission('iot:device:query')") - public CommonResult> getDevicePage(@Valid IotDevicePageReqVO pageReqVO) { - PageResult pageResult = deviceService.getDevicePage(pageReqVO); - return success(BeanUtils.toBean(pageResult, IotDeviceRespVO.class)); - } - - @GetMapping("/export-excel") - @Operation(summary = "导出设备 Excel") - @PreAuthorize("@ss.hasPermission('iot:device:export')") - @ApiAccessLog(operateType = EXPORT) - public void exportDeviceExcel(@Valid IotDevicePageReqVO exportReqVO, - HttpServletResponse response) throws IOException { - exportReqVO.setPageSize(PageParam.PAGE_SIZE_NONE); - CommonResult> result = getDevicePage(exportReqVO); - // 导出 Excel - ExcelUtils.write(response, "设备.xls", "数据", IotDeviceRespVO.class, - result.getData().getList()); - } - - @GetMapping("/count") - @Operation(summary = "获得设备数量") - @Parameter(name = "productId", description = "产品编号", example = "1") - @PreAuthorize("@ss.hasPermission('iot:device:query')") - public CommonResult getDeviceCount(@RequestParam("productId") Long productId) { - return success(deviceService.getDeviceCountByProductId(productId)); - } - - @GetMapping("/simple-list") - @Operation(summary = "获取设备的精简信息列表", description = "主要用于前端的下拉选项") - @Parameters({ - @Parameter(name = "deviceType", description = "设备类型", example = "1"), - @Parameter(name = "productId", description = "产品编号", example = "1024") - }) - public CommonResult> getDeviceSimpleList( - @RequestParam(value = "deviceType", required = false) Integer deviceType, - @RequestParam(value = "productId", required = false) Long productId) { - List list = deviceService.getDeviceListByCondition(deviceType, productId); - return success(convertList(list, device -> // 只返回 id、name、productId 字段 - new IotDeviceRespVO().setId(device.getId()).setDeviceName(device.getDeviceName()) - .setProductId(device.getProductId()).setState(device.getState()))); - } - - @GetMapping("/location-list") - @Operation(summary = "获取设备位置列表", description = "获取有经纬度信息的设备列表,用于地图展示") - @PreAuthorize("@ss.hasPermission('iot:device:query')") - public CommonResult> getDeviceLocationList() { - // 1. 获取有位置信息的设备列表 - List devices = deviceService.getDeviceListByHasLocation(); - if (CollUtil.isEmpty(devices)) { - return success(Collections.emptyList()); - } - - // 2. 转换并返回 - Map productMap = convertMap(productService.getProductList(), IotProductDO::getId); - return success(convertList(devices, device -> { - IotDeviceRespVO respVO = BeanUtils.toBean(device, IotDeviceRespVO.class); - MapUtils.findAndThen(productMap, device.getProductId(), - product -> respVO.setProductName(product.getName())); - return respVO; - })); - } - - @PostMapping("/import") - @Operation(summary = "导入设备") - @PreAuthorize("@ss.hasPermission('iot:device:import')") - public CommonResult importDevice( - @RequestParam("file") MultipartFile file, - @RequestParam(value = "updateSupport", required = false, defaultValue = "false") Boolean updateSupport) - throws Exception { - List list = ExcelUtils.read(file, IotDeviceImportExcelVO.class); - return success(deviceService.importDevice(list, updateSupport)); - } - - @GetMapping("/get-import-template") - @Operation(summary = "获得导入设备模板") - public void importTemplate(HttpServletResponse response) throws IOException { - // 手动创建导出 demo - List list = Arrays.asList( - IotDeviceImportExcelVO.builder().deviceName("温度传感器001").parentDeviceName("gateway110") - .productKey("1de24640dfe").groupNames("灰度分组,生产分组").build(), - IotDeviceImportExcelVO.builder().deviceName("biubiu").productKey("YzvHxd4r67sT4s2B") - .groupNames("").build()); - // 输出 - ExcelUtils.write(response, "设备导入模板.xls", "数据", IotDeviceImportExcelVO.class, list); - } - - @GetMapping("/get-auth-info") - @Operation(summary = "获得设备连接信息") - @PreAuthorize("@ss.hasPermission('iot:device:auth-info')") - public CommonResult getDeviceAuthInfo(@RequestParam("id") Long id) { - return success(deviceService.getDeviceAuthInfo(id)); - } - - // TODO @haohao:可以使用 @RequestParam("productKey") String productKey, @RequestParam("deviceNames") List deviceNames 来接收哇? - @GetMapping("/list-by-product-key-and-names") - @Operation(summary = "通过产品标识和设备名称列表获取设备") - @PreAuthorize("@ss.hasPermission('iot:device:query')") - public CommonResult> getDevicesByProductKeyAndNames(@Valid IotDeviceByProductKeyAndNamesReqVO reqVO) { - List devices = deviceService.getDeviceListByProductKeyAndNames(reqVO.getProductKey(), reqVO.getDeviceNames()); - return success(BeanUtils.toBean(devices, IotDeviceRespVO.class)); - } - -} diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/device/IotDeviceGroupController.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/device/IotDeviceGroupController.java deleted file mode 100644 index 99d81a9..0000000 --- a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/device/IotDeviceGroupController.java +++ /dev/null @@ -1,88 +0,0 @@ -package cn.iocoder.yudao.module.iot.controller.admin.device; - -import cn.iocoder.yudao.framework.common.enums.CommonStatusEnum; -import cn.iocoder.yudao.framework.common.pojo.CommonResult; -import cn.iocoder.yudao.framework.common.pojo.PageResult; -import cn.iocoder.yudao.framework.common.util.object.BeanUtils; -import cn.iocoder.yudao.module.iot.controller.admin.device.vo.group.IotDeviceGroupPageReqVO; -import cn.iocoder.yudao.module.iot.controller.admin.device.vo.group.IotDeviceGroupRespVO; -import cn.iocoder.yudao.module.iot.controller.admin.device.vo.group.IotDeviceGroupSaveReqVO; -import cn.iocoder.yudao.module.iot.dal.dataobject.device.IotDeviceGroupDO; -import cn.iocoder.yudao.module.iot.service.device.IotDeviceGroupService; -import cn.iocoder.yudao.module.iot.service.device.IotDeviceService; -import io.swagger.v3.oas.annotations.Operation; -import io.swagger.v3.oas.annotations.Parameter; -import io.swagger.v3.oas.annotations.tags.Tag; -import org.springframework.security.access.prepost.PreAuthorize; -import org.springframework.validation.annotation.Validated; -import org.springframework.web.bind.annotation.*; - -import javax.annotation.Resource; -import javax.validation.Valid; -import java.util.List; - -import static cn.iocoder.yudao.framework.common.pojo.CommonResult.success; -import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertList; - -@Tag(name = "管理后台 - IoT 设备分组") -@RestController -@RequestMapping("/iot/device-group") -@Validated -public class IotDeviceGroupController { - - @Resource - private IotDeviceGroupService deviceGroupService; - @Resource - private IotDeviceService deviceService; - - @PostMapping("/create") - @Operation(summary = "创建设备分组") - @PreAuthorize("@ss.hasPermission('iot:device-group:create')") - public CommonResult createDeviceGroup(@Valid @RequestBody IotDeviceGroupSaveReqVO createReqVO) { - return success(deviceGroupService.createDeviceGroup(createReqVO)); - } - - @PutMapping("/update") - @Operation(summary = "更新设备分组") - @PreAuthorize("@ss.hasPermission('iot:device-group:update')") - public CommonResult updateDeviceGroup(@Valid @RequestBody IotDeviceGroupSaveReqVO updateReqVO) { - deviceGroupService.updateDeviceGroup(updateReqVO); - return success(true); - } - - @DeleteMapping("/delete") - @Operation(summary = "删除设备分组") - @Parameter(name = "id", description = "编号", required = true) - @PreAuthorize("@ss.hasPermission('iot:device-group:delete')") - public CommonResult deleteDeviceGroup(@RequestParam("id") Long id) { - deviceGroupService.deleteDeviceGroup(id); - return success(true); - } - - @GetMapping("/get") - @Operation(summary = "获得设备分组") - @Parameter(name = "id", description = "编号", required = true, example = "1024") - @PreAuthorize("@ss.hasPermission('iot:device-group:query')") - public CommonResult getDeviceGroup(@RequestParam("id") Long id) { - IotDeviceGroupDO deviceGroup = deviceGroupService.getDeviceGroup(id); - return success(BeanUtils.toBean(deviceGroup, IotDeviceGroupRespVO.class)); - } - - @GetMapping("/page") - @Operation(summary = "获得设备分组分页") - @PreAuthorize("@ss.hasPermission('iot:device-group:query')") - public CommonResult> getDeviceGroupPage(@Valid IotDeviceGroupPageReqVO pageReqVO) { - PageResult pageResult = deviceGroupService.getDeviceGroupPage(pageReqVO); - return success(BeanUtils.toBean(pageResult, IotDeviceGroupRespVO.class, - group -> group.setDeviceCount(deviceService.getDeviceCountByGroupId(group.getId())))); - } - - @GetMapping("/simple-list") - @Operation(summary = "获取设备分组的精简信息列表", description = "只包含被开启的分组,主要用于前端的下拉选项") - public CommonResult> getSimpleDeviceGroupList() { - List list = deviceGroupService.getDeviceGroupListByStatus(CommonStatusEnum.ENABLE.getStatus()); - return success(convertList(list, group -> // 只返回 id、name 字段 - new IotDeviceGroupRespVO().setId(group.getId()).setName(group.getName()))); - } - -} \ No newline at end of file diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/device/IotDeviceMessageController.http b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/device/IotDeviceMessageController.http deleted file mode 100644 index 93c86e1..0000000 --- a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/device/IotDeviceMessageController.http +++ /dev/null @@ -1,101 +0,0 @@ -### 请求 /iot/device/message/send 接口(属性上报)=> 成功 -POST {{baseUrl}}/iot/device/message/send -Content-Type: application/json -tenant-id: {{adminTenantId}} -Authorization: Bearer {{token}} - -{ - "deviceId": 25, - "method": "thing.property.post", - "params": { - "width": 1, - "height": "2", - "oneThree": "3" - } -} - -### 请求 /iot/device/downstream 接口(服务调用)=> 成功 TODO 芋艿:未更新为最新 -POST {{baseUrl}}/iot/device/downstream -Content-Type: application/json -tenant-id: {{adminTenantId}} -Authorization: Bearer {{token}} - -{ - "id": 25, - "type": "service", - "identifier": "temperature", - "data": { - "xx": "yy" - } -} - -### 请求 /iot/device/downstream 接口(属性设置)=> 成功 TODO 芋艿:未更新为最新 -POST {{baseUrl}}/iot/device/downstream -Content-Type: application/json -tenant-id: {{adminTenantId}} -Authorization: Bearer {{token}} - -{ - "id": 25, - "type": "property", - "identifier": "set", - "data": { - "xx": "yy" - } -} - -### 请求 /iot/device/downstream 接口(属性获取)=> 成功 TODO 芋艿:未更新为最新 -POST {{baseUrl}}/iot/device/downstream -Content-Type: application/json -tenant-id: {{adminTenantId}} -Authorization: Bearer {{token}} - -{ - "id": 25, - "type": "property", - "identifier": "get", - "data": ["xx", "yy"] -} - -### 请求 /iot/device/downstream 接口(配置设置)=> 成功 TODO 芋艿:未更新为最新 -POST {{baseUrl}}/iot/device/downstream -Content-Type: application/json -tenant-id: {{adminTenantId}} -Authorization: Bearer {{token}} - -{ - "id": 25, - "type": "config", - "identifier": "set" -} - -### 请求 /iot/device/downstream 接口(OTA 升级)=> 成功 TODO 芋艿:未更新为最新 -POST {{baseUrl}}/iot/device/downstream -Content-Type: application/json -tenant-id: {{adminTenantId}} -Authorization: Bearer {{token}} - -{ - "id": 25, - "type": "ota", - "identifier": "upgrade", - "data": { - "firmwareId": 1, - "version": "1.0.0", - "signMethod": "MD5", - "fileSign": "d41d8cd98f00b204e9800998ecf8427e", - "fileSize": 1024, - "fileUrl": "http://example.com/firmware.bin", - "information": "{\"desc\":\"升级到最新版本\"}" - } -} - -### 查询设备消息对分页 - 基础查询(设备编号25) -GET {{baseUrl}}/iot/device/message/pair-page?deviceId=25&pageNo=1&pageSize=10 -Authorization: Bearer {{token}} -tenant-id: {{adminTenantId}} - -### 查询设备消息对分页 - 按标识符过滤(identifier=eat) -GET {{baseUrl}}/iot/device/message/pair-page?deviceId=25&identifier=eat&pageNo=1&pageSize=10 -Authorization: Bearer {{token}} -tenant-id: {{adminTenantId}} \ No newline at end of file diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/device/IotDeviceMessageController.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/device/IotDeviceMessageController.java deleted file mode 100644 index 1ed433f..0000000 --- a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/device/IotDeviceMessageController.java +++ /dev/null @@ -1,92 +0,0 @@ -package cn.iocoder.yudao.module.iot.controller.admin.device; - -import cn.hutool.core.collection.CollUtil; -import cn.iocoder.yudao.framework.common.pojo.CommonResult; -import cn.iocoder.yudao.framework.common.pojo.PageResult; -import cn.iocoder.yudao.framework.common.util.object.BeanUtils; -import cn.iocoder.yudao.module.iot.controller.admin.device.vo.message.IotDeviceMessagePageReqVO; -import cn.iocoder.yudao.module.iot.controller.admin.device.vo.message.IotDeviceMessageRespPairVO; -import cn.iocoder.yudao.module.iot.controller.admin.device.vo.message.IotDeviceMessageRespVO; -import cn.iocoder.yudao.module.iot.controller.admin.device.vo.message.IotDeviceMessageSendReqVO; -import cn.iocoder.yudao.module.iot.core.mq.message.IotDeviceMessage; -import cn.iocoder.yudao.module.iot.dal.dataobject.device.IotDeviceMessageDO; -import cn.iocoder.yudao.module.iot.dal.tdengine.IotDeviceMessageMapper; -import cn.iocoder.yudao.module.iot.service.device.IotDeviceService; -import cn.iocoder.yudao.module.iot.service.device.message.IotDeviceMessageService; -import cn.iocoder.yudao.module.iot.service.thingmodel.IotThingModelService; -import io.swagger.v3.oas.annotations.Operation; -import io.swagger.v3.oas.annotations.tags.Tag; -import org.springframework.security.access.prepost.PreAuthorize; -import org.springframework.validation.annotation.Validated; -import org.springframework.web.bind.annotation.*; - -import javax.annotation.Resource; -import javax.validation.Valid; -import java.util.List; -import java.util.Map; - -import static cn.iocoder.yudao.framework.common.pojo.CommonResult.success; -import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertList; -import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertMap; - -@Tag(name = "管理后台 - IoT 设备消息") -@RestController -@RequestMapping("/iot/device/message") -@Validated -public class IotDeviceMessageController { - - @Resource - private IotDeviceMessageService deviceMessageService; - @Resource - private IotDeviceService deviceService; - @Resource - private IotThingModelService thingModelService; - @Resource - private IotDeviceMessageMapper deviceMessageMapper; - - @GetMapping("/page") - @Operation(summary = "获得设备消息分页") - @PreAuthorize("@ss.hasPermission('iot:device:message-query')") - public CommonResult> getDeviceMessagePage( - @Valid IotDeviceMessagePageReqVO pageReqVO) { - PageResult pageResult = deviceMessageService.getDeviceMessagePage(pageReqVO); - return success(BeanUtils.toBean(pageResult, IotDeviceMessageRespVO.class)); - } - - @GetMapping("/pair-page") - @Operation(summary = "获得设备消息对分页") - @PreAuthorize("@ss.hasPermission('iot:device:message-query')") - public CommonResult> getDeviceMessagePairPage( - @Valid IotDeviceMessagePageReqVO pageReqVO) { - // 1.1 先按照条件,查询 request 的消息(非 reply) - pageReqVO.setReply(false); - PageResult requestMessagePageResult = deviceMessageService.getDeviceMessagePage(pageReqVO); - if (CollUtil.isEmpty(requestMessagePageResult.getList())) { - return success(PageResult.empty()); - } - // 1.2 接着按照 requestIds,批量查询 reply 消息 - List requestIds = convertList(requestMessagePageResult.getList(), IotDeviceMessageDO::getRequestId); - List replyMessageList = deviceMessageService.getDeviceMessageListByRequestIdsAndReply( - pageReqVO.getDeviceId(), requestIds, true); - Map replyMessages = convertMap(replyMessageList, IotDeviceMessageDO::getRequestId); - - // 2. 组装结果 - List pairMessages = convertList(requestMessagePageResult.getList(), - requestMessage -> { - IotDeviceMessageDO replyMessage = replyMessages.get(requestMessage.getRequestId()); - return new IotDeviceMessageRespPairVO() - .setRequest(BeanUtils.toBean(requestMessage, IotDeviceMessageRespVO.class)) - .setReply(BeanUtils.toBean(replyMessage, IotDeviceMessageRespVO.class)); - }); - return success(new PageResult<>(pairMessages, requestMessagePageResult.getTotal())); - } - - @PostMapping("/send") - @Operation(summary = "发送消息", description = "可用于设备模拟") - @PreAuthorize("@ss.hasPermission('iot:device:message-end')") - public CommonResult sendDeviceMessage(@Valid @RequestBody IotDeviceMessageSendReqVO sendReqVO) { - deviceMessageService.sendDeviceMessage(BeanUtils.toBean(sendReqVO, IotDeviceMessage.class)); - return success(true); - } - -} \ No newline at end of file diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/device/IotDeviceModbusConfigController.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/device/IotDeviceModbusConfigController.java deleted file mode 100644 index e736b68..0000000 --- a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/device/IotDeviceModbusConfigController.java +++ /dev/null @@ -1,55 +0,0 @@ -package cn.iocoder.yudao.module.iot.controller.admin.device; - -import cn.iocoder.yudao.framework.common.pojo.CommonResult; -import cn.iocoder.yudao.framework.common.util.object.BeanUtils; -import cn.iocoder.yudao.module.iot.controller.admin.device.vo.modbus.IotDeviceModbusConfigRespVO; -import cn.iocoder.yudao.module.iot.controller.admin.device.vo.modbus.IotDeviceModbusConfigSaveReqVO; -import cn.iocoder.yudao.module.iot.dal.dataobject.device.IotDeviceModbusConfigDO; -import cn.iocoder.yudao.module.iot.service.device.IotDeviceModbusConfigService; -import io.swagger.v3.oas.annotations.Operation; -import io.swagger.v3.oas.annotations.Parameter; -import io.swagger.v3.oas.annotations.tags.Tag; -import org.springframework.security.access.prepost.PreAuthorize; -import org.springframework.validation.annotation.Validated; -import org.springframework.web.bind.annotation.*; - -import javax.annotation.Resource; -import javax.validation.Valid; - -import static cn.iocoder.yudao.framework.common.pojo.CommonResult.success; - -@Tag(name = "管理后台 - IoT 设备 Modbus 连接配置") -@RestController -@RequestMapping("/iot/device-modbus-config") -@Validated -public class IotDeviceModbusConfigController { - - @Resource - private IotDeviceModbusConfigService modbusConfigService; - - @PostMapping("/save") - @Operation(summary = "保存设备 Modbus 连接配置") - @PreAuthorize("@ss.hasPermission('iot:device:update')") - public CommonResult saveDeviceModbusConfig(@Valid @RequestBody IotDeviceModbusConfigSaveReqVO saveReqVO) { - modbusConfigService.saveDeviceModbusConfig(saveReqVO); - return success(true); - } - - @GetMapping("/get") - @Operation(summary = "获得设备 Modbus 连接配置") - @Parameter(name = "id", description = "编号", example = "1024") - @Parameter(name = "deviceId", description = "设备编号", example = "2048") - @PreAuthorize("@ss.hasPermission('iot:device:query')") - public CommonResult getDeviceModbusConfig( - @RequestParam(value = "id", required = false) Long id, - @RequestParam(value = "deviceId", required = false) Long deviceId) { - IotDeviceModbusConfigDO modbusConfig = null; - if (id != null) { - modbusConfig = modbusConfigService.getDeviceModbusConfig(id); - } else if (deviceId != null) { - modbusConfig = modbusConfigService.getDeviceModbusConfigByDeviceId(deviceId); - } - return success(BeanUtils.toBean(modbusConfig, IotDeviceModbusConfigRespVO.class)); - } - -} diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/device/IotDeviceModbusPointController.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/device/IotDeviceModbusPointController.java deleted file mode 100644 index bf4e565..0000000 --- a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/device/IotDeviceModbusPointController.java +++ /dev/null @@ -1,73 +0,0 @@ -package cn.iocoder.yudao.module.iot.controller.admin.device; - -import cn.iocoder.yudao.framework.common.pojo.CommonResult; -import cn.iocoder.yudao.framework.common.pojo.PageResult; -import cn.iocoder.yudao.framework.common.util.object.BeanUtils; -import cn.iocoder.yudao.module.iot.controller.admin.device.vo.modbus.IotDeviceModbusPointPageReqVO; -import cn.iocoder.yudao.module.iot.controller.admin.device.vo.modbus.IotDeviceModbusPointRespVO; -import cn.iocoder.yudao.module.iot.controller.admin.device.vo.modbus.IotDeviceModbusPointSaveReqVO; -import cn.iocoder.yudao.module.iot.dal.dataobject.device.IotDeviceModbusPointDO; -import cn.iocoder.yudao.module.iot.service.device.IotDeviceModbusPointService; -import io.swagger.v3.oas.annotations.Operation; -import io.swagger.v3.oas.annotations.Parameter; -import io.swagger.v3.oas.annotations.tags.Tag; -import org.springframework.security.access.prepost.PreAuthorize; -import org.springframework.validation.annotation.Validated; -import org.springframework.web.bind.annotation.*; - -import javax.annotation.Resource; -import javax.validation.Valid; - -import static cn.iocoder.yudao.framework.common.pojo.CommonResult.success; - -@Tag(name = "管理后台 - IoT 设备 Modbus 点位配置") -@RestController -@RequestMapping("/iot/device-modbus-point") -@Validated -public class IotDeviceModbusPointController { - - @Resource - private IotDeviceModbusPointService modbusPointService; - - @PostMapping("/create") - @Operation(summary = "创建设备 Modbus 点位配置") - @PreAuthorize("@ss.hasPermission('iot:device:update')") - public CommonResult createDeviceModbusPoint(@Valid @RequestBody IotDeviceModbusPointSaveReqVO createReqVO) { - return success(modbusPointService.createDeviceModbusPoint(createReqVO)); - } - - @PutMapping("/update") - @Operation(summary = "更新设备 Modbus 点位配置") - @PreAuthorize("@ss.hasPermission('iot:device:update')") - public CommonResult updateDeviceModbusPoint(@Valid @RequestBody IotDeviceModbusPointSaveReqVO updateReqVO) { - modbusPointService.updateDeviceModbusPoint(updateReqVO); - return success(true); - } - - @DeleteMapping("/delete") - @Operation(summary = "删除设备 Modbus 点位配置") - @Parameter(name = "id", description = "编号", required = true) - @PreAuthorize("@ss.hasPermission('iot:device:update')") - public CommonResult deleteDeviceModbusPoint(@RequestParam("id") Long id) { - modbusPointService.deleteDeviceModbusPoint(id); - return success(true); - } - - @GetMapping("/get") - @Operation(summary = "获得设备 Modbus 点位配置") - @Parameter(name = "id", description = "编号", required = true, example = "1024") - @PreAuthorize("@ss.hasPermission('iot:device:query')") - public CommonResult getDeviceModbusPoint(@RequestParam("id") Long id) { - IotDeviceModbusPointDO modbusPoint = modbusPointService.getDeviceModbusPoint(id); - return success(BeanUtils.toBean(modbusPoint, IotDeviceModbusPointRespVO.class)); - } - - @GetMapping("/page") - @Operation(summary = "获得设备 Modbus 点位配置分页") - @PreAuthorize("@ss.hasPermission('iot:device:query')") - public CommonResult> getDeviceModbusPointPage(@Valid IotDeviceModbusPointPageReqVO pageReqVO) { - PageResult pageResult = modbusPointService.getDeviceModbusPointPage(pageReqVO); - return success(BeanUtils.toBean(pageResult, IotDeviceModbusPointRespVO.class)); - } - -} diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/device/IotDevicePropertyController.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/device/IotDevicePropertyController.java deleted file mode 100644 index 863d8cb..0000000 --- a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/device/IotDevicePropertyController.java +++ /dev/null @@ -1,89 +0,0 @@ -package cn.iocoder.yudao.module.iot.controller.admin.device; - -import cn.hutool.core.date.LocalDateTimeUtil; -import cn.hutool.core.lang.Assert; -import cn.iocoder.yudao.framework.common.pojo.CommonResult; -import cn.iocoder.yudao.module.iot.controller.admin.device.vo.property.IotDevicePropertyDetailRespVO; -import cn.iocoder.yudao.module.iot.controller.admin.device.vo.property.IotDevicePropertyHistoryListReqVO; -import cn.iocoder.yudao.module.iot.controller.admin.device.vo.property.IotDevicePropertyRespVO; -import cn.iocoder.yudao.module.iot.dal.dataobject.thingmodel.model.ThingModelProperty; -import cn.iocoder.yudao.module.iot.dal.dataobject.device.IotDeviceDO; -import cn.iocoder.yudao.module.iot.dal.dataobject.device.IotDevicePropertyDO; -import cn.iocoder.yudao.module.iot.dal.dataobject.thingmodel.IotThingModelDO; -import cn.iocoder.yudao.module.iot.enums.thingmodel.IotThingModelTypeEnum; -import cn.iocoder.yudao.module.iot.service.device.IotDeviceService; -import cn.iocoder.yudao.module.iot.service.device.property.IotDevicePropertyService; -import cn.iocoder.yudao.module.iot.service.thingmodel.IotThingModelService; -import io.swagger.v3.oas.annotations.Operation; -import io.swagger.v3.oas.annotations.Parameter; -import io.swagger.v3.oas.annotations.tags.Tag; -import org.springframework.security.access.prepost.PreAuthorize; -import org.springframework.validation.annotation.Validated; -import org.springframework.web.bind.annotation.GetMapping; -import org.springframework.web.bind.annotation.RequestMapping; -import org.springframework.web.bind.annotation.RequestParam; -import org.springframework.web.bind.annotation.RestController; - -import javax.annotation.Resource; -import javax.validation.Valid; -import java.util.List; -import java.util.Map; - -import static cn.iocoder.yudao.framework.common.pojo.CommonResult.success; -import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertList; - -@Tag(name = "管理后台 - IoT 设备属性") -@RestController -@RequestMapping("/iot/device/property") -@Validated -public class IotDevicePropertyController { - - @Resource - private IotDevicePropertyService devicePropertyService; - @Resource - private IotThingModelService thingModelService; - @Resource - private IotDeviceService deviceService; - - @GetMapping("/get-latest") - @Operation(summary = "获取设备属性最新属性") - @Parameter(name = "deviceId", description = "设备编号", required = true) - @PreAuthorize("@ss.hasPermission('iot:device:property-query')") - public CommonResult> getLatestDeviceProperties( - @RequestParam("deviceId") Long deviceId) { - // 1.1 获取设备信息 - IotDeviceDO device = deviceService.getDevice(deviceId); - Assert.notNull(device, "设备不存在"); - // 1.2 获取设备最新属性 - Map properties = devicePropertyService.getLatestDeviceProperties(deviceId); - // 1.3 根据 productId + type 查询属性类型的物模型 - List thingModels = thingModelService.getThingModelListByProductIdAndType( - device.getProductId(), IotThingModelTypeEnum.PROPERTY.getType()); - - // 2. 基于 thingModels 遍历,拼接 properties - return success(convertList(thingModels, thingModel -> { - ThingModelProperty thingModelProperty = thingModel.getProperty(); - Assert.notNull(thingModelProperty, "属性不能为空"); - IotDevicePropertyDetailRespVO result = new IotDevicePropertyDetailRespVO() - .setName(thingModel.getName()).setDataType(thingModelProperty.getDataType()) - .setDataSpecs(thingModelProperty.getDataSpecs()) - .setDataSpecsList(thingModelProperty.getDataSpecsList()); - result.setIdentifier(thingModel.getIdentifier()); - IotDevicePropertyDO property = properties.get(thingModel.getIdentifier()); - if (property != null) { - result.setValue(property.getValue()) - .setUpdateTime(LocalDateTimeUtil.toEpochMilli(property.getUpdateTime())); - } - return result; - })); - } - - @GetMapping("/history-list") - @Operation(summary = "获取设备属性历史数据列表") - @PreAuthorize("@ss.hasPermission('iot:device:property-query')") - public CommonResult> getHistoryDevicePropertyList( - @Valid IotDevicePropertyHistoryListReqVO listReqVO) { - return success(devicePropertyService.getHistoryDevicePropertyList(listReqVO)); - } - -} \ No newline at end of file diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/device/config/TDengineConfig.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/device/config/TDengineConfig.java new file mode 100644 index 0000000..262027d --- /dev/null +++ b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/device/config/TDengineConfig.java @@ -0,0 +1,23 @@ +package cn.iocoder.yudao.module.iot.controller.admin.device.config; + +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.springframework.jdbc.core.JdbcTemplate; + +import javax.sql.DataSource; + +@Configuration +public class TDengineConfig { + + @Bean("tdengineJdbcTemplate") + public JdbcTemplate tdengineJdbcTemplate(DataSource dataSource) { + // 此处的dataSource会自动注入上面在yml中配置的TDengine数据源 + return new JdbcTemplate(dataSource); + } + +// @Bean(name = "tdengineDataSource") +// @ConfigurationProperties(prefix = "spring.datasource.dynamic.datasource.tdengine") +// public DataSource tdengineDataSource() { +// return DataSourceBuilder.create().build(); +// } +} \ No newline at end of file diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/device/enums/DeviceBasicStatusEnum.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/device/enums/DeviceBasicStatusEnum.java new file mode 100644 index 0000000..0729370 --- /dev/null +++ b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/device/enums/DeviceBasicStatusEnum.java @@ -0,0 +1,78 @@ +package cn.iocoder.yudao.module.iot.controller.admin.device.enums; + +import lombok.AllArgsConstructor; +import lombok.Getter; + +/** + * 设备运行报警状态枚举 + */ +@Getter +@AllArgsConstructor +public enum DeviceBasicStatusEnum { + /** + * 运行状态 + */ + RUNNING("1", "运行", "RUNNING"), + + /** + * 报警状态 + */ + ALARM("2", "报警", "ALARM"); + + /** + * 状态编码 + */ + private final String code; + + /** + * 状态名称 + */ + private final String name; + + /** + * 状态描述 + */ + private final String description; + + /** + * 根据编码获取枚举 + */ + public static DeviceBasicStatusEnum getByCode(Integer code) { + if (code == null) { + return null; + } + for (DeviceBasicStatusEnum status : values()) { + if (status.getCode().equals(code)) { + return status; + } + } + return null; + } + + /** + * 根据描述获取枚举 + */ + public static DeviceBasicStatusEnum getByDescription(String description) { + if (description == null || description.trim().isEmpty()) { + return null; + } + for (DeviceBasicStatusEnum status : values()) { + if (status.getDescription().equalsIgnoreCase(description.trim())) { + return status; + } + } + return null; + } + + /** + * 检查编码是否有效 + */ + public static boolean isValidCode(Integer code) { + return getByCode(code) != null; + } + + @Override + public String toString() { + return this.name; + } +} \ No newline at end of file diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/device/enums/DeviceStatusEnum.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/device/enums/DeviceStatusEnum.java new file mode 100644 index 0000000..9c31b6f --- /dev/null +++ b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/device/enums/DeviceStatusEnum.java @@ -0,0 +1,72 @@ +package cn.iocoder.yudao.module.iot.controller.admin.device.enums; + +import lombok.AllArgsConstructor; +import lombok.Getter; + +/** + * 设备状态枚举 + */ +@Getter +@AllArgsConstructor +public enum DeviceStatusEnum { + + /** + * 离线 + */ + OFFLINE("0", "离线", "设备离线"), + + /** + * 运行 + */ + RUNNING("1", "运行", "设备正常运行"), + + /** + * 待机中(不运行、没故障) + */ + STANDBY("2", "待机中", "设备待机,无故障"), + + /** + * 故障中(故障且待机) + */ + FAULT_STANDBY("3", "故障中", "设备故障且处于待机状态"), + + /** + * 报警中(故障且运行) + */ + ALARM_RUNNING("4", "报警中", "设备故障但仍在运行"); + + /** + * 状态编码 + */ + private final String code; + + /** + * 状态名称 + */ + private final String name; + + /** + * 状态描述 + */ + private final String description; + + /** + * 根据状态码获取枚举 + */ + public static DeviceStatusEnum getByCode(String code) { + if (code == null) { + return null; + } + for (DeviceStatusEnum status : values()) { + if (status.getCode().equals(code)) { + return status; + } + } + return null; + } + + + + + +} \ No newline at end of file diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/device/enums/JavaToTdengineTypeEnum.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/device/enums/JavaToTdengineTypeEnum.java new file mode 100644 index 0000000..e2d595a --- /dev/null +++ b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/device/enums/JavaToTdengineTypeEnum.java @@ -0,0 +1,136 @@ +package cn.iocoder.yudao.module.iot.controller.admin.device.enums; + +/** + * Java 基本类型对应 TDengine 列类型枚举 + */ +public enum JavaToTdengineTypeEnum { + + BYTE("byte", "TINYINT") { + @Override + public Object convertValue(String value) { + return Byte.parseByte(value); + } + }, + + SHORT("short", "SMALLINT") { + @Override + public Object convertValue(String value) { + return Short.parseShort(value); + } + }, + + INT("int", "INT") { + @Override + public Object convertValue(String value) { + return Integer.parseInt(value); + } + }, + + LONG("long", "BIGINT") { + @Override + public Object convertValue(String value) { + return Long.parseLong(value); + } + }, + + FLOAT("float", "FLOAT") { + @Override + public Object convertValue(String value) { + return Float.parseFloat(value); + } + }, + + DOUBLE("double", "DOUBLE") { + @Override + public Object convertValue(String value) { + return Double.parseDouble(value); + } + }, + + BOOLEAN("boolean", "BOOL") { + @Override + public Object convertValue(String value) { + return Boolean.parseBoolean(value); + } + }, + + CHAR("char", "NCHAR(1)") { + @Override + public Object convertValue(String value) { + return value.charAt(0); + } + }; + + private final String javaType; + private final String tdType; + + JavaToTdengineTypeEnum(String javaType, String tdType) { + this.javaType = javaType; + this.tdType = tdType; + } + + public String getJavaType() { + return javaType; + } + + public String getTdType() { + return tdType; + } + + /** + * 每个枚举实现自己的值转换 + */ + public abstract Object convertValue(String value); + + /** + * 根据 javaType 获取枚举 + */ + public static JavaToTdengineTypeEnum fromJavaType(String javaType) { + + if (javaType == null) { + return null; + } + + for (JavaToTdengineTypeEnum e : values()) { + + if (e.javaType.equalsIgnoreCase(javaType)) { + return e; + } + } + + return null; + } + + /** + * 根据 javaType 获取 TDengine 类型 + */ + public static String getTdTypeByJavaType(String javaType) { + + JavaToTdengineTypeEnum e = fromJavaType(javaType); + + return e != null ? e.getTdType() : null; + } + + /** + * 根据 javaType 转换 value + */ + public static Object convertValue(String javaType, String value) { + + try { + + JavaToTdengineTypeEnum e = fromJavaType(javaType); + + if (e == null) { + return Double.parseDouble(value); + } + + return e.convertValue(value); + + } catch (Exception ex) { + + throw new RuntimeException( + "数据类型转换失败: javaType=" + javaType + ", value=" + value, ex + ); + } + } +} diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/device/enums/TaskTypeEnum.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/device/enums/TaskTypeEnum.java new file mode 100644 index 0000000..1642fdd --- /dev/null +++ b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/device/enums/TaskTypeEnum.java @@ -0,0 +1,60 @@ +// TaskTypeEnum.java +package cn.iocoder.yudao.module.iot.controller.admin.device.enums; + +import lombok.AllArgsConstructor; +import lombok.Getter; + +@Getter +@AllArgsConstructor +public enum TaskTypeEnum { + + DEVICE("DEVICE", "设备数据采集"), + WORK_ORDER("WORK_ORDER", "工单生成"); + + /** + * 任务类型编码 + */ + private final String code; + + /** + * 任务类型名称 + */ + private final String name; + + /** + * 根据编码获取枚举 + */ + public static TaskTypeEnum getByCode(String code) { + for (TaskTypeEnum type : values()) { + if (type.getCode().equals(code)) { + return type; + } + } + return null; + } + + /** + * 检查编码是否存在 + */ + public static boolean contains(String code) { + return getByCode(code) != null; + } + + /** + * 生成任务ID + */ + public Long generateTaskId(Long baseId) { + if (baseId == null) { + baseId = 0L; + } + + switch (this) { + case DEVICE: + return 1000000L + baseId; + case WORK_ORDER: + return 2000000L + baseId; + default: + return 9000000L + baseId; + } + } +} \ No newline at end of file diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/device/scheduled/config/SchedulerConfig.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/device/scheduled/config/SchedulerConfig.java new file mode 100644 index 0000000..89539d1 --- /dev/null +++ b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/device/scheduled/config/SchedulerConfig.java @@ -0,0 +1,28 @@ +package cn.iocoder.yudao.module.iot.controller.admin.device.scheduled.config; + +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.springframework.scheduling.TaskScheduler; +import org.springframework.scheduling.annotation.EnableScheduling; +import org.springframework.scheduling.concurrent.ThreadPoolTaskScheduler; + +@Configuration +@EnableScheduling +public class SchedulerConfig { + + @Bean + public TaskScheduler taskScheduler() { + ThreadPoolTaskScheduler scheduler = new ThreadPoolTaskScheduler(); + scheduler.setPoolSize(50); // 增加线程数 + scheduler.setThreadNamePrefix("scheduled-task-"); + scheduler.setAwaitTerminationSeconds(60); + scheduler.setWaitForTasksToCompleteOnShutdown(true); + + // 设置队列容量 + scheduler.setPoolSize(50); + scheduler.setThreadPriority(Thread.NORM_PRIORITY); + scheduler.setDaemon(false); + scheduler.initialize(); + return scheduler; + } +} \ No newline at end of file diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/device/scheduled/core/Task.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/device/scheduled/core/Task.java new file mode 100644 index 0000000..b1ae69c --- /dev/null +++ b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/device/scheduled/core/Task.java @@ -0,0 +1,14 @@ +package cn.iocoder.yudao.module.iot.controller.admin.device.scheduled.core; + +public interface Task { + + /** + * 执行任务 + */ + void execute(Long taskId, String taskParam); + + /** + * 获取任务类型 + */ + String getTaskType(); +} diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/device/scheduled/coretask/DeviceTask.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/device/scheduled/coretask/DeviceTask.java new file mode 100644 index 0000000..f1d6f33 --- /dev/null +++ b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/device/scheduled/coretask/DeviceTask.java @@ -0,0 +1,722 @@ +//// DeviceTask.java - 原有设备任务 +//package cn.iocoder.yudao.module.iot.controller.admin.device.scheduled.coretask; +// +//import cn.iocoder.yudao.framework.common.enums.DeviceConnectionStatusEnum; +//import cn.iocoder.yudao.module.iot.controller.admin.device.enums.DeviceBasicStatusEnum; +//import cn.iocoder.yudao.module.iot.controller.admin.device.enums.DeviceStatusEnum; +//import cn.iocoder.yudao.module.iot.controller.admin.device.enums.TaskTypeEnum; +//import cn.iocoder.yudao.module.iot.controller.admin.device.scheduled.core.Task; +//import cn.iocoder.yudao.module.iot.controller.admin.device.scheduled.scheduler.TaskSchedulerManager; +//import cn.iocoder.yudao.module.iot.controller.admin.devicemodelrules.vo.PointRulesRespVO; +//import cn.iocoder.yudao.module.iot.dal.dataobject.device.DeviceDO; +//import cn.iocoder.yudao.module.iot.dal.dataobject.devicecontactmodel.DeviceContactModelDO; +//import cn.iocoder.yudao.module.iot.dal.dataobject.deviceoperationrecord.DeviceOperationRecordDO; +//import cn.iocoder.yudao.module.iot.dal.dataobject.devicepointrules.DevicePointRulesDO; +//import cn.iocoder.yudao.module.iot.dal.dataobject.devicewarinningrecord.DeviceWarinningRecordDO; +//import cn.iocoder.yudao.module.iot.dal.mysql.device.DeviceMapper; +//import cn.iocoder.yudao.module.iot.dal.mysql.devicecontactmodel.DeviceContactModelMapper; +//import cn.iocoder.yudao.module.iot.dal.mysql.deviceoperationrecord.DeviceOperationRecordMapper; +//import cn.iocoder.yudao.module.iot.dal.mysql.devicepointrules.DevicePointRulesMapper; +//import cn.iocoder.yudao.module.iot.dal.mysql.devicewarinningrecord.DeviceWarinningRecordMapper; +//import cn.iocoder.yudao.module.iot.service.device.DeviceService; +//import cn.iocoder.yudao.module.iot.service.device.TDengineService; +//import com.alibaba.fastjson.JSON; +//import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; +//import com.baomidou.mybatisplus.core.toolkit.CollectionUtils; +//import com.baomidou.mybatisplus.core.toolkit.Wrappers; +//import lombok.extern.slf4j.Slf4j; +//import org.apache.commons.lang3.StringUtils; +//import org.slf4j.Logger; +//import org.slf4j.LoggerFactory; +//import org.springframework.context.annotation.Lazy; +//import org.springframework.stereotype.Component; +// +//import javax.annotation.Resource; +//import java.text.SimpleDateFormat; +//import java.util.ArrayList; +//import java.util.Date; +//import java.util.List; +// +//@Component +//@Slf4j +//public class DeviceTask implements Task { +// private static final Logger logger = LoggerFactory.getLogger(DeviceTask.class); +// +// @Resource +// private DeviceContactModelMapper deviceContactModelMapper; +// +// @Resource +// private DeviceMapper deviceMapper; +// +// @Resource +// private TDengineService tDengineService; +// +// @Resource +// @Lazy +// private DeviceService deviceService; +// +// @Resource +// private DevicePointRulesMapper devicePointRulesMapper; +// +// @Resource +// private DeviceOperationRecordMapper deviceOperationRecordMapper; +// +// @Resource +// private DeviceWarinningRecordMapper deviceWarinningRecordMapper; +// +// @Resource +// @Lazy +// private TaskSchedulerManager taskSchedulerManager; +// +// @Override +// public String getTaskType() { +// return TaskTypeEnum.DEVICE.getCode(); +// } +// +// +//// static { +//// // 定期清理过期连接 +//// ScheduledExecutorService cleanupExecutor = Executors.newSingleThreadScheduledExecutor(); +//// cleanupExecutor.scheduleAtFixedRate(DeviceTask::cleanupExpiredConnections, +//// CLEANUP_INTERVAL, CLEANUP_INTERVAL, TimeUnit.MILLISECONDS); +//// } +// +//// // 会话信息 +//// private static class SessionInfo { +//// long lastUsedTime; +//// String deviceUrl; +//// String username; +//// +//// SessionInfo(String deviceUrl, String username) { +//// this.deviceUrl = deviceUrl; +//// this.username = username; +//// this.lastUsedTime = System.currentTimeMillis(); +//// } +//// +//// boolean isExpired() { +//// return System.currentTimeMillis() - lastUsedTime > CONNECTION_TIMEOUT; +//// } +//// +//// void updateLastUsed() { +//// this.lastUsedTime = System.currentTimeMillis(); +//// } +//// } +// +// +// @Override +// public void execute(Long taskId, String taskParam) { +// Long deviceId = taskId - 1000000L; +// +// try { +// SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss"); +// String currentTime = sdf.format(new Date()); +// +// logger.info("执行设备任务,任务ID: {}, 参数: {}, 时间: {}", +// taskId, taskParam, currentTime); +// +// executeDeviceLogic(taskId, taskParam); +// +// +// } catch (Exception e) { +// logger.error("执行设备任务异常,任务ID: {}", taskId, e); +// } +//// finally { +//// //确保出问题不会打满opcv服务器连接数 +//// OpcUtils.disconnect(); +//// } +// } +// +// /** +// * 具体的设备执行逻辑 +// */ +// private void executeDeviceLogic(Long sourceDeviceId, String param) { +// logger.info("执行设备逻辑,源设备ID: {},参数: {}", sourceDeviceId, param); +// +// // 1. 计算实际设备ID +// Long deviceId = sourceDeviceId - 1000000L; +// logger.info("处理后设备ID: {}", deviceId); +// +// // 2. 获取设备信息 +// DeviceDO device = getDeviceInfo(deviceId); +// +// // 3. 连接OPC服务器 +// connectOpcServer(device); +// +// // 4. 处理数据读取和入库 +// processDeviceData(deviceId, device); +// +// // 5. 断开连接 +//// OpcUtils.disconnect(); +// } +// +// +// /** +// * 获取设备信息 +// */ +// private DeviceDO getDeviceInfo(Long deviceId) { +// DeviceDO device = deviceMapper.selectById(deviceId); +// +// if (device == null) { +// throw new RuntimeException("设备不存在,ID: " + deviceId); +// } +// +// if (StringUtils.isBlank(device.getUrl())) { +// throw new RuntimeException("设备URL不能为空,ID: " + deviceId); +// } +// +// return device; +// } +// +// /** +// * 连接OPC服务器 +// */ +// private void connectOpcServer(DeviceDO device) { +// String username = StringUtils.defaultString(device.getUsername()); +// String password = StringUtils.defaultString(device.getPassword()); +// +// boolean connected = false; +// +// +// try { +// connected = OpcUtils.connect( +// device.getId(), +// device.getUrl(), +// username, +// password, +// 10 +// ); +// if (!connected) { +// +// log.error("设备 {} 连接OPC服务器失败,URL: {}", device.getId(), device.getUrl()); +// device.setStatus(String.valueOf(DeviceConnectionStatusEnum.DISCONNECTED.getStatus())); +// deviceMapper.updateById(device); +// taskSchedulerManager.stopDeviceTask(device.getId()); +// DeviceOperationRecordDO deviceOperationRecordDO = new DeviceOperationRecordDO(); +// deviceOperationRecordDO.setDeviceId(device.getId()); +// deviceOperationRecordDO.setRule(DeviceStatusEnum.OFFLINE.getCode()); +// //TODO 默认内置管理员 +// deviceOperationRecordDO.setCreator("1"); +// deviceOperationRecordDO.setUpdater("1"); +// deviceOperationRecordMapper.insert(deviceOperationRecordDO); +// //抛出异常终止任务 +// throw new RuntimeException("连接opcuv服务器异常"); +// +// } +// +// log.info("设备 {} 成功连接OPC服务器,URL: {}", device.getId(), device.getUrl()); +// +// } catch (Exception e) { +// log.error("设备 {} 连接OPC服务器异常,URL: {}", device.getId(), device.getUrl(), e); +// device.setStatus(String.valueOf(DeviceConnectionStatusEnum.DISCONNECTED.getStatus())); +// deviceMapper.updateById(device); +// taskSchedulerManager.stopDeviceTask(device.getId()); +// DeviceOperationRecordDO deviceOperationRecordDO = new DeviceOperationRecordDO(); +// deviceOperationRecordDO.setDeviceId(device.getId()); +// deviceOperationRecordDO.setRule(DeviceStatusEnum.OFFLINE.getCode()); +// //TODO 默认内置管理员 +// deviceOperationRecordDO.setCreator("1"); +// deviceOperationRecordDO.setUpdater("1"); +// deviceOperationRecordMapper.insert(deviceOperationRecordDO); +// //抛出异常终止任务 +// throw new RuntimeException(e); +// } +// +// +// } +// +// /** +// * 处理设备数据 - 安全批量读取 OPC UA 点位 +// */ +// private void processDeviceData(Long deviceId, DeviceDO device) { +// // 1. 查询点位配置 +// List points = getDevicePoints(deviceId); +// if (CollectionUtils.isEmpty(points)) { +// logger.warn("设备 {} 未配置点位", deviceId); +// // 更新状态为待机中 +// DeviceDO deviceDO = deviceMapper.selectById(deviceId); +// DeviceOperationRecordDO record = new DeviceOperationRecordDO(); +// record.setDeviceId(deviceId); +// record.setRule(DeviceStatusEnum.STANDBY.getCode()); +// record.setTotalStandbyTime(deviceDO.getSampleCycle()); +// record.setCreator("1"); +// record.setUpdater("1"); +// deviceOperationRecordMapper.insert(record); +// return; +// } +// +// logger.info("设备 {} 需要读取 {} 个点位", deviceId, points.size()); +// +// // 2. 构建有效地址列表及索引映射,确保顺序对应 +// List addresses = new ArrayList<>(); +// List indexMap = new ArrayList<>(); // 对应原 points 的索引 +// for (int i = 0; i < points.size(); i++) { +// String address = StringUtils.trimToEmpty(points.get(i).getAddress()); +// if (!address.isEmpty()) { +// addresses.add(address); +// indexMap.add(i); +// } +// } +// +// // 3. 批量读取 OPC UA 点位 +// List values = new ArrayList<>(); +// try { +// if (!addresses.isEmpty()) { +// values = OpcUtils.readValues(device.getId(), addresses); +// } +// } catch (Exception e) { +// logger.error("设备 {} 批量读取 OPC UA 点位异常", deviceId, e); +// } +// +// // 4. 处理读取到的数据 +// int successCount = 0; +// List validDataList = new ArrayList<>(); +// for (int i = 0; i < values.size(); i++) { +// DeviceContactModelDO point = points.get(indexMap.get(i)); +// Object value = values.get(i); +// +// try { +// String processedValue = processOpcValue(value); +// +// // 规则判断和告警/运行记录处理 +// judgmentRules(processedValue, point.getAttributeCode(), device, point.getId()); +// +// point.setAddressValue(processedValue.isEmpty() ? null : processedValue); +// +// if (point.getAddressValue() != null) { +// successCount++; +// } +// validDataList.add(point); +// +// } catch (Exception e) { +// logger.error("处理点位 {} 异常,地址: {}", point.getId(), point.getAddress(), e); +// } +// } +// +// // 5. 入库处理 +// if (!validDataList.isEmpty()) { +// saveToDatabase(deviceId, validDataList, successCount); +// } else { +// logger.warn("设备 {} 未读取到有效数据", deviceId); +// } +// } +// +// +// /** +// * 获取设备点位 +// */ +// private List getDevicePoints(Long deviceId) { +// LambdaQueryWrapper query = new LambdaQueryWrapper<>(); +// query.eq(DeviceContactModelDO::getDeviceId, deviceId); +// return deviceContactModelMapper.selectList(query); +// } +// +//// /** +//// * 处理单个点位 +//// */ +//// private void processSinglePoint(DeviceContactModelDO point, List validDataList, DeviceDO device) { +//// try { +//// String address = StringUtils.trimToEmpty(point.getAddress()); +//// if (address.isEmpty()) { +//// logger.warn("点位ID {} 地址为空", point.getId()); +//// return; +//// } +//// +//// Object value = OpcUtils.readValue(device.getId(),address); +////// if (value == null) { +//// logger.info("读取点位 {} ,地址: {}", point.getId(), address); +////// } else { +//// String processedValue = processOpcValue(value); +//// +//// //判断规则 +//// judgmentRules(processedValue,point.getAttributeCode(),device,point.getId()); +//// point.setAddressValue(processedValue.isEmpty() ? null : processedValue); +//// +////// } +//// +//// validDataList.add(point); +//// } catch (Exception e) { +//// logger.error("处理点位 {} 异常,地址: {}", +//// point.getId(), point.getAddress(), e); +//// } +//// } +// +// +// /** +// * 处理OPC值 +// */ +// private String processOpcValue(Object value) { +// if (value == null) { +// return ""; +// } +// +// if (value instanceof String) { +// return ((String) value).trim(); +// } +// +// return value.toString(); +// } +// +// /** +// * 保存到数据库 +// */ +// private void saveToDatabase(Long deviceId, List dataList, int successCount) { +// try { +// for (DeviceContactModelDO deviceContactModelDO : dataList) { +// deviceContactModelDO.setAddress(null); +// } +// String json = JSON.toJSONString(dataList); +// boolean inserted = tDengineService.insertDeviceData(deviceId, json); +// +// if (inserted) { +// logger.info("设备 {} 数据入库成功,总数: {},有效: {}", +// deviceId, dataList.size(), successCount); +// } else { +// logger.error("设备 {} 数据入库失败", deviceId); +// } +// } catch (Exception e) { +// logger.error("设备 {} 数据入库异常", deviceId, e); +// } +// } +// +// private void judgmentRules(String processedValue, String attributeCode, DeviceDO device, Long modelId) { +// if (StringUtils.isBlank(processedValue)) { +// logger.warn("待判断的值为空,编码attributeCode: {}, deviceId: {}", attributeCode, device.getId()); +//// return; +// } +// +// // 1. 查询设备规则 +// List devicePointRulesDOList = devicePointRulesMapper.selectList( +// Wrappers.lambdaQuery() +// .eq(DevicePointRulesDO::getDeviceId, device.getId()).orderByDesc(DevicePointRulesDO::getCreateTime)); +// +// if (CollectionUtils.isEmpty(devicePointRulesDOList)) { +// logger.debug("设备 {} 未配置规则", device.getId()); +// return; +// } +// +// // 2. 遍历规则 +// for (DevicePointRulesDO devicePointRulesDO : devicePointRulesDOList) { +// if (StringUtils.isBlank(devicePointRulesDO.getFieldRule())) { +// continue; +// } +// +// // 3. 解析规则列表 +// List pointRulesVOList = JSON.parseArray( +// devicePointRulesDO.getFieldRule(), PointRulesRespVO.class); +// +// if (CollectionUtils.isEmpty(pointRulesVOList)) { +// continue; +// } +// +// // 4. 找到对应modelId的规则并进行判断 +// for (PointRulesRespVO pointRulesRespVO : pointRulesVOList) { +// if (pointRulesRespVO.getCode() != null && +// pointRulesRespVO.getCode().equals(attributeCode)) { +// +// boolean matched = matchRule(processedValue, pointRulesRespVO); +// +// if (matched) { +// logger.info("规则匹配成功: modelId={}, value={}, rule={}", +// attributeCode, processedValue, +// JSON.toJSONString(pointRulesRespVO)); +// +// // 执行匹配成功后的逻辑 +// handleMatchedSuccessRule(devicePointRulesDO, pointRulesRespVO, processedValue, device, attributeCode, modelId); +// break; +// } else { +// logger.debug("规则不匹配: modelId={}, value={}, rule={}", +// attributeCode, processedValue, +// JSON.toJSONString(pointRulesRespVO)); +// // 执行匹配失败后的逻辑 +// handleMatchedFailureRule(devicePointRulesDO, pointRulesRespVO, processedValue, device, attributeCode); +// +// } +// } +// } +// } +// } +// +// private void handleMatchedFailureRule(DevicePointRulesDO devicePointRulesDO, PointRulesRespVO pointRulesRespVO, String processedValue, DeviceDO device, String attributeCode) { +// //TODO 离线待优化 +//// if (devicePointRulesDO.getIdentifier().equals(DeviceBasicStatusEnum.RUNNING.getCode())){ +//// DeviceOperationRecordDO record = new DeviceOperationRecordDO(); +//// record.setDeviceId(device.getId()); +//// record.setModelId(modelId); +//// record.setRule(pointRulesRespVO.getRule()); +//// record.setAddressValue(processedValue); +//// record.setRecordType(getRecordType(devicePointRulesDO)); +//// record.setRuleId(devicePointRulesDO.getId()); +//// +//// } +// +// +// } +// +// private void handleMatchedSuccessRule(DevicePointRulesDO devicePointRulesDO, +// PointRulesRespVO pointRulesRespVO, +// String processedValue, +// DeviceDO device, +// String attributeCode, +// Long modelId) { +// DeviceContactModelDO deviceContactModelDO = deviceContactModelMapper.selectById(modelId); +// if (deviceContactModelDO == null) { +// return; +// } +// +// +// //分别处理运行记录和告警记录 +// if (StringUtils.isBlank(devicePointRulesDO.getAlarmLevel())) { +// DeviceOperationRecordDO record = new DeviceOperationRecordDO(); +// record.setDeviceId(device.getId()); +// record.setModelId(modelId); +// record.setRule(pointRulesRespVO.getRule()); +// record.setAddressValue(processedValue); +// record.setRecordType(getRecordType(devicePointRulesDO)); +// record.setRuleId(devicePointRulesDO.getId()); +// //TODO 创建人和更新人为内置默认管理员 +// record.setCreator("1"); +// record.setUpdater("1"); +// +// // 处理累计时间 +// calculateAndSetTotalTime(record, pointRulesRespVO.getRule(), device.getSampleCycle()); +// +// deviceOperationRecordMapper.insert(record); +// } else { +// +// DeviceWarinningRecordDO deviceWarinningRecordDO = new DeviceWarinningRecordDO(); +// deviceWarinningRecordDO.setDeviceId(device.getId()); +// deviceWarinningRecordDO.setModelId(modelId); +// deviceWarinningRecordDO.setRule(pointRulesRespVO.getRule()); +// deviceWarinningRecordDO.setAlarmLevel(devicePointRulesDO.getAlarmLevel()); +// deviceWarinningRecordDO.setAddressValue(processedValue); +// deviceWarinningRecordDO.setRuleId(devicePointRulesDO.getId()); +// deviceWarinningRecordDO.setDeviceName(device.getDeviceName()); +// deviceWarinningRecordDO.setModelName(deviceContactModelDO.getAttributeName()); +// deviceWarinningRecordDO.setRuleName(devicePointRulesDO.getFieldName()); +// //TODO 创建人和更新人为内置默认管理员 +// deviceWarinningRecordDO.setCreator("1"); +// deviceWarinningRecordDO.setUpdater("1"); +// deviceWarinningRecordMapper.insert(deviceWarinningRecordDO); +// } +// } +// +// private void calculateAndSetTotalTime(DeviceOperationRecordDO record, String ruleCode, Double sampleCycle) { +// if (!isTimeRelatedStatus(ruleCode)) { +// return; +// } +// +//// DeviceOperationRecordDO lastRecord = deviceOperationRecordMapper.selectOne( +//// Wrappers.lambdaQuery() +//// .eq(DeviceOperationRecordDO::getRule, ruleCode) +//// .orderByDesc(DeviceOperationRecordDO::getCreateTime) +//// .last("LIMIT 1") +//// ); +// +// if (ruleCode.equals(DeviceStatusEnum.RUNNING.getCode())) { +//// Double totalTime = (lastRecord != null && lastRecord.getTotalRunningTime() != null) +//// ? lastRecord.getTotalRunningTime() + sampleCycle +//// : sampleCycle; +// record.setTotalRunningTime(sampleCycle); +// +// } else if (ruleCode.equals(DeviceStatusEnum.STANDBY.getCode())) { +//// Double totalTime = (lastRecord != null && lastRecord.getTotalStandbyTime() != null) +//// ? lastRecord.getTotalStandbyTime() + sampleCycle +//// : sampleCycle; +// record.setTotalStandbyTime(sampleCycle); +// +// } else if (ruleCode.equals(DeviceStatusEnum.FAULT_STANDBY.getCode())) { +//// Double totalTime = (lastRecord != null && lastRecord.getTotalFaultTime() != null) +//// ? lastRecord.getTotalFaultTime() + sampleCycle +//// : sampleCycle; +// record.setTotalFaultTime(sampleCycle); +// +// } else if (ruleCode.equals(DeviceStatusEnum.ALARM_RUNNING.getCode())) { +//// Double totalTime = (lastRecord != null && lastRecord.getTotalWarningTime() != null) +//// ? lastRecord.getTotalWarningTime() + sampleCycle +//// : sampleCycle; +// record.setTotalWarningTime(sampleCycle); +// } +// } +// +// private Integer getRecordType(DevicePointRulesDO devicePointRulesDO) { +// return devicePointRulesDO.getIdentifier() +// .equals(DeviceBasicStatusEnum.RUNNING.getDescription()) +// ? Integer.parseInt(DeviceBasicStatusEnum.RUNNING.getCode()) +// : Integer.parseInt(DeviceBasicStatusEnum.ALARM.getCode()); +// } +// +// private boolean isTimeRelatedStatus(String ruleCode) { +// return ruleCode.equals(DeviceStatusEnum.RUNNING.getCode()) || +// ruleCode.equals(DeviceStatusEnum.STANDBY.getCode()) || +// ruleCode.equals(DeviceStatusEnum.FAULT_STANDBY.getCode()) || +// ruleCode.equals(DeviceStatusEnum.ALARM_RUNNING.getCode()); +// } +// +// +// /** +// * 判断值是否符合规则 +// * 支持操作符: EQ(等于), NE(不等于), GT(大于), GE(大于等于), +// * LT(小于), LE(小于等于), TRUE(为真), FALSE(为假) +// */ +// private boolean matchRule(String value, PointRulesRespVO rule) { +// if (StringUtils.isBlank(value) || rule == null || +// StringUtils.isBlank(rule.getOperator())) { +// return false; +// } +// +// try { +// String operator = rule.getOperator().toUpperCase(); +// String inputValue = value.trim().toLowerCase(); +// String ruleValue = StringUtils.trimToEmpty(rule.getOperatorRule()); +// +// // 1. 处理布尔值判断 +// if ("TRUE".equals(operator) || "FALSE".equals(operator)) { +// return matchBooleanRule(inputValue, operator); +// } +// +// // 2. 如果operatorRule为空,且不是布尔操作符,则返回false +// if (StringUtils.isBlank(ruleValue)) { +// logger.warn("规则比较值为空,但操作符不是布尔类型: operator={}", operator); +// return false; +// } +// +// ruleValue = ruleValue.trim(); +// +// // 3. 尝试数值比较 +// if (isNumeric(inputValue) && isNumeric(ruleValue)) { +// Double num1 = Double.parseDouble(inputValue); +// Double num2 = Double.parseDouble(ruleValue); +// +// return compareNumbers(num1, num2, operator); +// } +// // 4. 字符串比较 +// else { +// return compareStrings(inputValue, ruleValue, operator); +// } +// +// } catch (Exception e) { +// logger.error("规则匹配异常: value={}, rule={}, error={}", +// value, JSON.toJSONString(rule), e.getMessage()); +// return false; +// } +// } +// +// /** +// * 处理布尔值判断 +// */ +// private boolean matchBooleanRule(String value, String operator) { +// // 常见布尔值表示 +// boolean booleanValue = parseBoolean(value); +// +// if ("TRUE".equals(operator)) { +// return booleanValue; +// } else if ("FALSE".equals(operator)) { +// return !booleanValue; +// } +// +// return false; +// } +// +// /** +// * 解析字符串为布尔值 +// * 支持: true, false, 1, 0, yes, no, on, off等 +// */ +// private boolean parseBoolean(String value) { +// if (StringUtils.isBlank(value)) { +// return false; +// } +// +// String lowerValue = value.toLowerCase(); +// +// // 常见真值表示 +// if ("true".equals(lowerValue) || +// "1".equals(lowerValue) || +// "yes".equals(lowerValue) || +// "on".equals(lowerValue) || +// "是".equals(lowerValue) || // 中文支持 +// "成功".equals(lowerValue)) { +// return true; +// } +// +// // 常见假值表示 +// if ("false".equals(lowerValue) || +// "0".equals(lowerValue) || +// "no".equals(lowerValue) || +// "off".equals(lowerValue) || +// "否".equals(lowerValue) || // 中文支持 +// "失败".equals(lowerValue)) { +// return false; +// } +// +// // 尝试转换为布尔值 +// try { +// return Boolean.parseBoolean(lowerValue); +// } catch (Exception e) { +// logger.warn("无法解析为布尔值: {}", value); +// return false; +// } +// } +// +// /** +// * 数值比较 +// */ +// private boolean compareNumbers(Double value, Double ruleValue, String operator) { +// switch (operator) { +// case "EQ": +// return Math.abs(value - ruleValue) < 0.000001; // 处理浮点数精度 +// case "NE": +// return Math.abs(value - ruleValue) >= 0.000001; +// case "GT": +// return value > ruleValue; +// case "GE": +// return value >= ruleValue; +// case "LT": +// return value < ruleValue; +// case "LE": +// return value <= ruleValue; +// default: +// logger.warn("不支持的操作符: {}", operator); +// return false; +// } +// } +// +// /** +// * 字符串比较 +// */ +// private boolean compareStrings(String value, String ruleValue, String operator) { +// switch (operator) { +// case "EQ": +// return value.equals(ruleValue); +// case "NE": +// return !value.equals(ruleValue); +// case "GT": +// return value.compareTo(ruleValue) > 0; +// case "GE": +// return value.compareTo(ruleValue) >= 0; +// case "LT": +// return value.compareTo(ruleValue) < 0; +// case "LE": +// return value.compareTo(ruleValue) <= 0; +// default: +// logger.warn("不支持的操作符: {}", operator); +// return false; +// } +// } +// +// /** +// * 判断字符串是否为数字 +// */ +// private boolean isNumeric(String str) { +// if (StringUtils.isBlank(str)) { +// return false; +// } +// try { +// Double.parseDouble(str); +// return true; +// } catch (NumberFormatException e) { +// return false; +// } +// } +// +// +//} \ No newline at end of file diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/device/scheduled/scheduler/TaskSchedulerManager.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/device/scheduled/scheduler/TaskSchedulerManager.java new file mode 100644 index 0000000..657a847 --- /dev/null +++ b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/device/scheduled/scheduler/TaskSchedulerManager.java @@ -0,0 +1,277 @@ +// TaskSchedulerManager.java +package cn.iocoder.yudao.module.iot.controller.admin.device.scheduled.scheduler; + +import cn.iocoder.yudao.module.iot.controller.admin.device.enums.TaskTypeEnum; +import cn.iocoder.yudao.module.iot.controller.admin.device.scheduled.core.Task; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.scheduling.TaskScheduler; +import org.springframework.scheduling.support.CronTrigger; +import org.springframework.stereotype.Component; + +import java.util.Map; +import java.util.concurrent.ConcurrentHashMap; +import java.util.concurrent.ScheduledFuture; +import java.util.concurrent.TimeUnit; + +@Component +public class TaskSchedulerManager { + private static final Logger logger = LoggerFactory.getLogger(TaskSchedulerManager.class); + + private final TaskScheduler taskScheduler; + + // 存储所有任务 + private final Map taskBeans = new ConcurrentHashMap<>(); + + // 存储正在运行的任务 + private final Map> runningTasks = new ConcurrentHashMap<>(); + private final Map taskInfos = new ConcurrentHashMap<>(); + + // 任务信息类 + private static class TaskInfo { + private Long taskId; + private String taskType; + private String taskParam; + private String cronExpression; + private String scheduleType; // "CRON" 或 "FIXED_RATE" 或 "FIXED_DELAY" + private Long initialDelay; + private Long period; + private TimeUnit timeUnit; + + // 构造器 + public TaskInfo(Long taskId, String taskType, String taskParam) { + this.taskId = taskId; + this.taskType = taskType; + this.taskParam = taskParam; + } + + // getters and setters... + } + + // 通过构造器注入 + public TaskSchedulerManager(TaskScheduler taskScheduler, + @Autowired(required = false) java.util.List tasks) { + this.taskScheduler = taskScheduler; + + // 注册所有任务 + if (tasks != null) { + for (Task task : tasks) { + taskBeans.put(task.getTaskType(), task); + logger.info("注册任务类型: {}", task.getTaskType()); + } + } + } + + /** + * 启动 Cron 定时任务 + */ + public boolean startCronTask(Long taskId, String taskType, String taskParam, String cronExpression) { + return startTask(taskId, taskType, taskParam, "CRON", cronExpression, null, null, null); + } + + /** + * 启动固定频率任务 + */ + public boolean startFixedRateTask(Long taskId, String taskType, String taskParam, + long initialDelay, long period, TimeUnit timeUnit) { + return startTask(taskId, taskType, taskParam, "FIXED_RATE", null, + initialDelay, period, timeUnit); + } + + /** + * 启动固定延迟任务 + */ + public boolean startFixedDelayTask(Long taskId, String taskType, String taskParam, + long initialDelay, long period, TimeUnit timeUnit) { + return startTask(taskId, taskType, taskParam, "FIXED_DELAY", null, + initialDelay, period, timeUnit); + } + + /** + * 通用启动任务方法 + */ + private boolean startTask(Long taskId, String taskType, String taskParam, + String scheduleType, String cronExpression, + Long initialDelay, Long period, TimeUnit timeUnit) { + try { + // 停止已存在的任务 + stopTask(taskId); + + // 获取任务实例 + Task task = taskBeans.get(taskType); + if (task == null) { + logger.error("任务类型不存在: {}", taskType); + return false; + } + + // 创建任务信息 + TaskInfo taskInfo = new TaskInfo(taskId, taskType, taskParam); + taskInfo.scheduleType = scheduleType; + taskInfo.cronExpression = cronExpression; + taskInfo.initialDelay = initialDelay; + taskInfo.period = period; + taskInfo.timeUnit = timeUnit; + taskInfos.put(taskId, taskInfo); + + // 根据调度类型创建任务 + ScheduledFuture future = null; + switch (scheduleType) { + case "CRON": + future = taskScheduler.schedule( + () -> task.execute(taskId, taskParam), + new CronTrigger(cronExpression) + ); + break; + + case "FIXED_RATE": + if (initialDelay != null && period != null && timeUnit != null) { + future = taskScheduler.scheduleAtFixedRate( + () -> task.execute(taskId, taskParam), + initialDelay + ); + } + break; + + case "FIXED_DELAY": + if (initialDelay != null && period != null && timeUnit != null) { + future = taskScheduler.scheduleWithFixedDelay( + () -> task.execute(taskId, taskParam), + initialDelay + ); + } + break; + } + + if (future != null) { + runningTasks.put(taskId, future); + logger.info("启动{}任务成功,任务ID: {}, 类型: {}, 参数: {}", + scheduleType, taskId, taskType, taskParam); + return true; + } + + return false; + + } catch (Exception e) { + logger.error("启动任务失败,任务ID: {}", taskId, e); + return false; + } + } + + // TaskSchedulerManager.java + public boolean stopTask(Long taskId) { + try { + ScheduledFuture future = runningTasks.get(taskId); + if (future != null) { + future.cancel(true); + runningTasks.remove(taskId); + taskInfos.remove(taskId); + logger.info("停止任务成功,任务ID: {}", taskId); + return true; + } + return false; + } catch (Exception e) { + logger.error("停止任务失败,任务ID: {}", taskId, e); + return false; + } + } + + /** + * 获取任务信息 + */ + public TaskInfo getTaskInfo(Long taskId) { + return taskInfos.get(taskId); + } + + /** + * 检查任务是否运行 + */ + public boolean isTaskRunning(Long taskId) { + ScheduledFuture future = runningTasks.get(taskId); + return future != null && !future.isCancelled() && !future.isDone(); + } + + /** + * 获取所有任务类型 + */ + public Map getAllTaskTypes() { + return new ConcurrentHashMap<>(taskBeans); + } + + /** + * 获取运行中的任务数量 + */ + public int getRunningTaskCount() { + return runningTasks.size(); + } + + /** + * 停止所有任务 + */ + public void stopAllTasks() { + logger.info("停止所有定时任务,共 {} 个", runningTasks.size()); + for (Long taskId : runningTasks.keySet()) { + stopTask(taskId); + } + } + +// /** +// * 原设备任务方法(保持兼容性) +// */ +// public boolean startDeviceTask(Long deviceId, String deviceCode, String cronExpression) { +// String taskParam = deviceId + ":" + deviceCode; +// return startCronTask(deviceId, TaskTypeEnum.DEVICE.getCode(), taskParam, cronExpression); +// } + + /** + * 启动设备任务 + */ + public boolean startDeviceTask(Long deviceId, String cronExpression) { + Long taskId = TaskTypeEnum.DEVICE.generateTaskId(deviceId); + return startCronTask(taskId, TaskTypeEnum.DEVICE.getCode(), + "device:" + deviceId, cronExpression); + } + + /** + * 停止设备任务 + */ + public boolean stopDeviceTask(Long deviceId) { + if (deviceId == null) { + logger.warn("设备ID不能为空"); + return false; + } + + // 生成与启动时相同的任务ID + Long taskId = TaskTypeEnum.DEVICE.generateTaskId(deviceId); + logger.info("停止设备任务,设备ID: {}, 生成的任务ID: {}", deviceId, taskId); + + return stopTask(taskId); + } + + + /** + * 启动工单任务 + */ + public boolean startWorkOrderTask(Long configId, String cronExpression) { + Long taskId = TaskTypeEnum.WORK_ORDER.generateTaskId(configId); + return startCronTask(taskId, TaskTypeEnum.WORK_ORDER.getCode(), + "workOrder:" + configId, cronExpression); + } + + + /** + * 停止工单任务 + */ + public boolean stopWorkOrderTask(Long configId) { + if (configId == null) { + logger.warn("工单配置ID不能为空"); + return false; + } + + // 生成与启动时相同的任务ID + Long taskId = TaskTypeEnum.WORK_ORDER.generateTaskId(configId); + logger.info("停止工单任务,配置ID: {}, 生成的任务ID: {}", configId, taskId); + + return stopTask(taskId); + } +} \ No newline at end of file diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/device/scheduled/utils/CronExpressionUtils.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/device/scheduled/utils/CronExpressionUtils.java new file mode 100644 index 0000000..b197f59 --- /dev/null +++ b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/device/scheduled/utils/CronExpressionUtils.java @@ -0,0 +1,160 @@ +package cn.iocoder.yudao.module.iot.controller.admin.device.scheduled.utils; + +import org.springframework.scheduling.support.CronTrigger; +import org.springframework.stereotype.Component; +import org.springframework.util.StringUtils; + +@Component +public class CronExpressionUtils { + + /** + * 将秒数转换为cron表达式 + * @param intervalSeconds 间隔秒数 + * @return cron表达式 + */ + public static String secondsToCron(Double intervalSeconds) { + if (intervalSeconds == null || intervalSeconds <= 0) { + throw new IllegalArgumentException("间隔秒数必须大于0"); + } + + long seconds = Math.round(intervalSeconds); + + // 如果秒数小于60,使用秒级定时 + if (seconds < 60) { + return String.format("0/%d * * * * ?", seconds); + } + + // 如果秒数在60-3599之间,使用分钟级定时 + else if (seconds < 3600) { + long minutes = seconds / 60; + if (seconds % 60 != 0) { + // 如果不是整分钟,需要特殊处理 + return String.format("0 */%d * * * ?", minutes); + } + return String.format("0 0/%d * * * ?", minutes); + } + + // 如果秒数在3600-86399之间,使用小时级定时 + else if (seconds < 86400) { + long hours = seconds / 3600; + if (seconds % 3600 != 0) { + // 如果不是整小时,转换为分钟 + long minutes = seconds / 60; + return String.format("0 0/%d * * * ?", minutes); + } + return String.format("0 0 0/%d * * ?", hours); + } + + // 如果大于等于1天,使用天级定时 + else { + long days = seconds / 86400; + if (seconds % 86400 != 0) { + // 如果不是整天,转换为小时 + long hours = seconds / 3600; + return String.format("0 0 0/%d * * ?", hours); + } + return String.format("0 0 0 */%d * ?", days); + } + } + + /** + * 将秒数转换为固定延迟的cron表达式(从0秒开始) + * @param intervalSeconds 间隔秒数 + * @return cron表达式 + */ + public String secondsToFixedRateCron(Double intervalSeconds) { + if (intervalSeconds == null || intervalSeconds <= 0) { + throw new IllegalArgumentException("间隔秒数必须大于0"); + } + + long seconds = Math.round(intervalSeconds); + + // 计算分钟和剩余秒数 + long minutes = seconds / 60; + long remainingSeconds = seconds % 60; + + long remainingMinutes; + if (minutes == 0) { + // 小于1分钟,只使用秒 + return String.format("*/%d * * * * ?", seconds); + } else if (minutes < 60) { + // 小于1小时 + if (remainingSeconds == 0) { + return String.format("0 */%d * * * ?", minutes); + } else { + // 有剩余秒数,需要更复杂的表达式 + return String.format("%d */%d * * * ?", remainingSeconds, minutes); + } + } else if (minutes < 1440) { // 小于24小时 + long hours = minutes / 60; + remainingMinutes = minutes % 60; + + if (remainingMinutes == 0 && remainingSeconds == 0) { + return String.format("0 0 */%d * * ?", hours); + } else if (remainingSeconds == 0) { + return String.format("0 %d */%d * * ?", remainingMinutes, hours); + } else { + return String.format("%d %d */%d * * ?", remainingSeconds, remainingMinutes, hours); + } + } else { + // 大于等于1天 + long days = minutes / 1440; + long remainingHours = (minutes % 1440) / 60; + remainingMinutes = minutes % 60; + + if (remainingHours == 0 && remainingMinutes == 0 && remainingSeconds == 0) { + return String.format("0 0 0 */%d * ?", days); + } else { + // 简化处理,转换为小时 + long totalHours = (days * 24) + remainingHours; + if (remainingMinutes == 0 && remainingSeconds == 0) { + return String.format("0 0 */%d * * ?", totalHours); + } else { + return String.format("%d %d */%d * * ?", + remainingSeconds, remainingMinutes, totalHours); + } + } + } + } + + /** + * 验证cron表达式是否有效 + * @param cronExpression cron表达式 + * @return 是否有效 + */ + public boolean isValidCron(String cronExpression) { + if (!StringUtils.hasText(cronExpression)) { + return false; + } + + try { + new CronTrigger(cronExpression); + return true; + } catch (Exception e) { + return false; + } + } + + /** + * 获取友好的时间描述 + * @param intervalSeconds 间隔秒数 + * @return 时间描述 + */ + public String getIntervalDescription(Double intervalSeconds) { + if (intervalSeconds == null) { + return "未设置"; + } + + long seconds = Math.round(intervalSeconds); + + if (seconds < 60) { + return seconds + "秒"; + } else if (seconds < 3600) { + return (seconds / 60) + "分" + (seconds % 60) + "秒"; + } else if (seconds < 86400) { + return (seconds / 3600) + "小时" + ((seconds % 3600) / 60) + "分"; + } else { + return (seconds / 86400) + "天" + ((seconds % 86400) / 3600) + "小时"; + } + } +} \ No newline at end of file diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/device/utils/DataTypeParseUtil.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/device/utils/DataTypeParseUtil.java new file mode 100644 index 0000000..0f1cc41 --- /dev/null +++ b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/device/utils/DataTypeParseUtil.java @@ -0,0 +1,162 @@ +package cn.iocoder.yudao.module.iot.controller.admin.device.utils; + +import lombok.extern.slf4j.Slf4j; + +import java.math.BigDecimal; +import java.sql.Timestamp; +import java.time.LocalDateTime; + +/** + * 数据类型解析工具类(静态工具类) + * + * 支持 TDengine / MQTT / OPC / JDBC 数据类型转换 + */ +@Slf4j +public final class DataTypeParseUtil { + + /** + * 私有构造函数,防止实例化 + */ + private DataTypeParseUtil() { + throw new UnsupportedOperationException("Utility class cannot be instantiated"); + } + + /** + * 根据 dataType 解析 value + * + * @param value 原始值 + * @param dataType 数据类型 + * @return 转换后的值 + */ + public static Object parse(Object value, String dataType) { + + if (value == null || dataType == null) { + return null; + } + + try { + + switch (dataType.toLowerCase()) { + + case "int": + case "integer": + return toInt(value); + + case "long": + return toLong(value); + + case "double": + return toDouble(value); + + case "float": + return toFloat(value); + + case "short": + return toShort(value); + + case "boolean": + return toBoolean(value); + + case "string": + case "varchar": + case "text": + return value.toString(); + + case "bigdecimal": + return toBigDecimal(value); + + case "timestamp": + case "datetime": + return toLocalDateTime(value); + + default: + return value; + } + + } catch (Exception e) { + + log.warn("数据类型解析失败 value={}, dataType={}", value, dataType); + + return value; + } + } + + public static Integer toInt(Object value) { + + if (value instanceof Number) { + return ((Number) value).intValue(); + } + + return Integer.parseInt(value.toString()); + } + + public static Long toLong(Object value) { + + if (value instanceof Number) { + return ((Number) value).longValue(); + } + + return Long.parseLong(value.toString()); + } + + public static Double toDouble(Object value) { + + if (value instanceof Number) { + return ((Number) value).doubleValue(); + } + + return Double.parseDouble(value.toString()); + } + + public static Float toFloat(Object value) { + + if (value instanceof Number) { + return ((Number) value).floatValue(); + } + + return Float.parseFloat(value.toString()); + } + + public static Short toShort(Object value) { + + if (value instanceof Number) { + return ((Number) value).shortValue(); + } + + return Short.parseShort(value.toString()); + } + + public static Boolean toBoolean(Object value) { + + if (value instanceof Boolean) { + return (Boolean) value; + } + + String str = value.toString().toLowerCase(); + + return "true".equals(str) || "1".equals(str); + } + + public static BigDecimal toBigDecimal(Object value) { + + if (value instanceof BigDecimal) { + return (BigDecimal) value; + } + + return new BigDecimal(value.toString()); + } + + public static LocalDateTime toLocalDateTime(Object value) { + + if (value instanceof Timestamp) { + return ((Timestamp) value).toLocalDateTime(); + } + + if (value instanceof LocalDateTime) { + return (LocalDateTime) value; + } + + return LocalDateTime.parse(value.toString()); + } + +} diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/device/vo/DeviceOperationStatusRespVO.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/device/vo/DeviceOperationStatusRespVO.java new file mode 100644 index 0000000..da93533 --- /dev/null +++ b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/device/vo/DeviceOperationStatusRespVO.java @@ -0,0 +1,36 @@ +package cn.iocoder.yudao.module.iot.controller.admin.device.vo; + + +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.Data; +import lombok.ToString; + +@Schema(description = "管理后台 - 设备运行状态 Resp VO") +@Data +@ToString(callSuper = true) +public class DeviceOperationStatusRespVO { + + @Schema(description = "总设备数") + private int totalDevices; + + @Schema(description = "运行") + private int runningCount; + + @Schema(description = "待机中(不运行、没故障)") + private int standbyCount; + + @Schema(description = "故障中(故障且待机)") + private int faultCount; + + @Schema(description = "报警中(故障且运行)") + private int warningCount; + + @Schema(description = "利用率") + private String utilizationRate; + + @Schema(description = "故障率") + private String faultRate; + +} + + diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/device/vo/DevicePageReqVO.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/device/vo/DevicePageReqVO.java new file mode 100644 index 0000000..0224a03 --- /dev/null +++ b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/device/vo/DevicePageReqVO.java @@ -0,0 +1,92 @@ +package cn.iocoder.yudao.module.iot.controller.admin.device.vo; + +import cn.iocoder.yudao.framework.common.pojo.PageParam; +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.Data; +import lombok.EqualsAndHashCode; +import lombok.ToString; +import org.springframework.format.annotation.DateTimeFormat; + +import java.time.LocalDateTime; + +import static cn.iocoder.yudao.framework.common.util.date.DateUtils.FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND; + +@Schema(description = "管理后台 - 物联设备分页 Request VO") +@Data +@EqualsAndHashCode(callSuper = true) +@ToString(callSuper = true) +public class DevicePageReqVO extends PageParam { + + @Schema(description = "设备编号") + private String deviceCode; + + @Schema(description = "设备名称", example = "赵六") + private String deviceName; + + @Schema(description = "设备类型", example = "2") + private String deviceType; + + @Schema(description = "状态", example = "1") + private String status; + + @Schema(description = "读主题") + private String readTopic; + + @Schema(description = "写主题") + private String writeTopic; + + @Schema(description = "网关id", example = "16311") + private Long gatewayId; + + @Schema(description = "设备品牌id", example = "24731") + private Long deviceBrandId; + + @Schema(description = "离线间隔") + private Long offLineDuration; + + @Schema(description = "最后上线时间") + @DateTimeFormat(pattern = FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND) + private LocalDateTime[] lastOnlineTime; + + @Schema(description = "备注", example = "你说的对") + private String remark; + + @Schema(description = "是否启用") + private Boolean isEnable; + + @Schema(description = "创建时间") + @DateTimeFormat(pattern = FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND) + private LocalDateTime[] createTime; + + @Schema(description = "关联设备模型", example = "1") + private Long deviceModelId; + + @Schema(description = "通讯协议", example = "OPCUA") + private String protocol; + + @Schema(description = "采集周期", example = "30") + private Double sampleCycle; + + @Schema(description = "端点url", example = "opc.tcp://127.0.0.1:48401") + private String url; + + @Schema(description = "用户名", example = "admin") + private String username; + + @Schema(description = "密码", example = "1234") + private String password; + + @Schema(description = "id集合导出用") + private String ids; + + @Schema(description = "mqtt订阅主题") + private String topic; + + @Schema(description = "客户Id") + private Long customerId; + + @Schema(description = "客户组织节点Id") + private Long orgNodeId; + + +} \ No newline at end of file diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/device/vo/DevicePointRespVO.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/device/vo/DevicePointRespVO.java new file mode 100644 index 0000000..6544322 --- /dev/null +++ b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/device/vo/DevicePointRespVO.java @@ -0,0 +1,26 @@ +package cn.iocoder.yudao.module.iot.controller.admin.device.vo; + + +import cn.iocoder.yudao.module.iot.dal.dataobject.devicecontactmodel.DeviceContactModelDO; +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.Data; +import lombok.ToString; + +import java.util.List; + +@Schema(description = "管理后台 - 设备参数集合列表 Resp VO") +@Data +@ToString(callSuper = true) +public class DevicePointRespVO { + + @Schema(description = "设备Id") + private Long deviceId; + + @Schema(description = "设备名称") + private String deviceName; + + @Schema(description = "子集参数列表") + private List contactModelDOList; + + +} diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/device/vo/DeviceRespVO.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/device/vo/DeviceRespVO.java new file mode 100644 index 0000000..4af131c --- /dev/null +++ b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/device/vo/DeviceRespVO.java @@ -0,0 +1,130 @@ +package cn.iocoder.yudao.module.iot.controller.admin.device.vo; + +import cn.iocoder.yudao.framework.excel.core.annotations.DictFormat; +import com.alibaba.excel.annotation.*; +import com.alibaba.excel.annotation.write.style.ColumnWidth; +import com.fasterxml.jackson.annotation.JsonFormat; +import com.fasterxml.jackson.databind.annotation.JsonDeserialize; +import com.fasterxml.jackson.databind.annotation.JsonSerialize; +import com.fasterxml.jackson.datatype.jsr310.deser.LocalDateTimeDeserializer; +import com.fasterxml.jackson.datatype.jsr310.ser.LocalDateTimeSerializer; +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.Data; + +import java.time.LocalDateTime; + +@Schema(description = "管理后台 - 物联设备 Response VO") +@Data +@ExcelIgnoreUnannotated +public class DeviceRespVO { + + @Schema(description = "ID", requiredMode = Schema.RequiredMode.REQUIRED, example = "26404") +// @ExcelProperty("ID") + private Long id; + + @Schema(description = "设备编号") + @ExcelProperty("设备编号") + private String deviceCode; + + @Schema(description = "设备名称", example = "赵六") + @ExcelProperty("设备名称") + private String deviceName; + + @Schema(description = "设备类型", example = "2") +// @ExcelProperty(value = "设备类型", converter = DictConvert.class) + @DictFormat("iot_device_type") // TODO 代码优化:建议设置到对应的 DictTypeConstants 枚举类中 + private String deviceType; + + @Schema(description = "状态", example = "1") +// @ExcelProperty(value = "连接状态", converter = DictConvert.class) + @DictFormat("iot_gateway_status") // TODO 代码优化:建议设置到对应的 DictTypeConstants 枚举类中 + private String status; + + @Schema(description = "读主题") +// @ExcelProperty("读主题") + private String readTopic; + + @Schema(description = "写主题") +// @ExcelProperty("写主题") + private String writeTopic; + + @Schema(description = "网关id", requiredMode = Schema.RequiredMode.REQUIRED, example = "16311") +// @ExcelProperty("网关id") + private Long gatewayId; + + @Schema(description = "设备品牌id", requiredMode = Schema.RequiredMode.REQUIRED, example = "24731") +// @ExcelProperty("设备品牌id") + private Long deviceBrandId; + + @Schema(description = "离线间隔", requiredMode = Schema.RequiredMode.REQUIRED) +// @ExcelProperty("离线间隔") + private Long offLineDuration; + + @Schema(description = "最后上线时间", requiredMode = Schema.RequiredMode.REQUIRED) +// @ExcelProperty("最后上线时间") + private LocalDateTime lastOnlineTime; + + @Schema(description = "备注", example = "你说的对") +// @ExcelProperty("备注") + private String remark; + + @Schema(description = "是否启用", requiredMode = Schema.RequiredMode.REQUIRED) +// @ExcelProperty(value = "是否启用", converter = DictConvert.class) + @DictFormat("infra_boolean_string") // TODO 代码优化:建议设置到对应的 DictTypeConstants 枚举类中 + private Boolean isEnable; + + @Schema(description = "创建时间", requiredMode = Schema.RequiredMode.REQUIRED) + @ExcelProperty("创建时间") + @ColumnWidth(20) // 设置此列的宽度为 20 个字符的宽度 + private LocalDateTime createTime; + + @Schema(description = "关联设备模型", example = "1") +// @ExcelProperty("关联设备模型") + private Long deviceModelId; + + @Schema(description = "通讯协议", example = "OPCUA") + @ExcelProperty("通讯协议") + private String protocol; + + @Schema(description = "采集周期", example = "30") +// @ExcelProperty("采集周期") + private Double sampleCycle; + + @Schema(description = "端点url", example = "opc.tcp://127.0.0.1:48401") +// @ExcelProperty("端点url") + private String url; + + @Schema(description = "用户名", example = "admin") +// @ExcelProperty("用户名") + private String username; + + @Schema(description = "密码", example = "1234") +// @ExcelProperty("密码") + private String password; + + @Schema(description = "运行状态") + @ExcelProperty("运行状态") + private String operatingStatus; + + + @Schema(description = "采集时间") + @ExcelProperty("采集时间") + @ColumnWidth(20) + @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss") + @JsonSerialize(using = LocalDateTimeSerializer.class) + @JsonDeserialize(using = LocalDateTimeDeserializer.class) + private LocalDateTime collectionTime; + + @Schema(description = "关联组织", example = "1") + private Long org; + + @Schema(description = "mqtt订阅主题") + @ExcelProperty("mqtt订阅主题") + private String topic; + + @Schema(description = "客户Id") + private Long customerId; + + @Schema(description = "客户组织节点Id") + private Long orgNodeId; +} \ No newline at end of file diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/device/vo/DeviceSaveReqVO.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/device/vo/DeviceSaveReqVO.java new file mode 100644 index 0000000..173fa0a --- /dev/null +++ b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/device/vo/DeviceSaveReqVO.java @@ -0,0 +1,88 @@ +package cn.iocoder.yudao.module.iot.controller.admin.device.vo; + +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.Data; + +import javax.validation.constraints.NotBlank; +import javax.validation.constraints.NotNull; +import java.time.LocalDateTime; + +@Data +public class DeviceSaveReqVO { + + @Schema(description = "ID", requiredMode = Schema.RequiredMode.REQUIRED, example = "26404") + private Long id; + + @Schema(description = "设备编号") + @NotBlank(message = "设备编号不能为空") // 设备编号通常是字符串,且不能是纯空格 + private String deviceCode; + + @Schema(description = "设备名称", example = "赵六") + @NotBlank(message = "设备名称不能为空") // 设备编号通常是字符串,且不能是纯空格 + private String deviceName; + + @Schema(description = "设备类型", example = "2") + private String deviceType; + + @Schema(description = "状态", example = "1") + private String status; + + @Schema(description = "读主题") + private String readTopic; + + @Schema(description = "写主题") + private String writeTopic; + + @Schema(description = "网关id", requiredMode = Schema.RequiredMode.REQUIRED, example = "16311") + private Long gatewayId; + + @Schema(description = "设备品牌id", requiredMode = Schema.RequiredMode.REQUIRED, example = "24731") + private Long deviceBrandId; + + @Schema(description = "离线间隔", requiredMode = Schema.RequiredMode.REQUIRED) + private Long offLineDuration; + + @Schema(description = "最后上线时间", requiredMode = Schema.RequiredMode.REQUIRED) + private LocalDateTime lastOnlineTime; + + @Schema(description = "备注", example = "你说的对") + private String remark; + + @Schema(description = "是否启用", requiredMode = Schema.RequiredMode.REQUIRED) + @NotNull(message = "是否启用不能为空") + private Boolean isEnable; + + @Schema(description = "关联设备模型", example = "1") + @NotNull(message = "关联设备模型ID不能为空") + private Long deviceModelId; + + @Schema(description = "通讯协议", example = "OPCUA") + private String protocol; + + @Schema(description = "采集周期", example = "30") + private Double sampleCycle; + + @Schema(description = "端点url", example = "opc.tcp://127.0.0.1:48401") + private String url; + + @Schema(description = "用户名", example = "admin") + private String username; + + @Schema(description = "密码", example = "1234") + private String password; + + @Schema(description = "是否连接1-连接 2-断开连接", example = "1") +// @NotNull + private Integer isConnect; + + @Schema(description = "mqtt订阅主题") + private String topic; + + @Schema(description = "客户Id") + private Long customerId; + + @Schema(description = "客户组织节点Id") + private Long orgNodeId; + + +} \ No newline at end of file diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/device/vo/DeviceUpdateEnabledReqVO.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/device/vo/DeviceUpdateEnabledReqVO.java new file mode 100644 index 0000000..811950a --- /dev/null +++ b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/device/vo/DeviceUpdateEnabledReqVO.java @@ -0,0 +1,20 @@ +package cn.iocoder.yudao.module.iot.controller.admin.device.vo; + +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.Data; + +import javax.validation.constraints.NotNull; + + +@Schema(description = "采集设备 - 更新启用状态 Request VO") +@Data +public class DeviceUpdateEnabledReqVO { + + @Schema(description = "任务ID", required = true, example = "1024") + @NotNull(message = "任务ID不能为空") + private Long id; + + @Schema(description = "是否启用", required = true, example = "true") + @NotNull(message = "启用状态不能为空") + private Boolean enabled; +} diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/device/vo/LineCodeAndNameRespVO.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/device/vo/LineCodeAndNameRespVO.java new file mode 100644 index 0000000..09f9f91 --- /dev/null +++ b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/device/vo/LineCodeAndNameRespVO.java @@ -0,0 +1,13 @@ +package cn.iocoder.yudao.module.iot.controller.admin.device.vo; + +import lombok.Data; + +@Data +public class LineCodeAndNameRespVO { + + private Long deviceId; + + private String lineCode; + + private String lineName; +} diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/device/vo/LineDeviceRequestVO.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/device/vo/LineDeviceRequestVO.java new file mode 100644 index 0000000..2dee3fa --- /dev/null +++ b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/device/vo/LineDeviceRequestVO.java @@ -0,0 +1,42 @@ +package cn.iocoder.yudao.module.iot.controller.admin.device.vo; + +import cn.iocoder.yudao.framework.common.pojo.PageParam; +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.Data; + +import java.time.LocalDateTime; + +@Data +public class LineDeviceRequestVO extends PageParam { + + @Schema(description = "ID", requiredMode = Schema.RequiredMode.REQUIRED, example = "26404") + private Long id; + + @Schema(description = "产线编码") + private String lineNode; + + @Schema(description = "产线名称") + private String lineName; + + @Schema(description = "设备编码") + private String deviceCode; + + @Schema(description = "设备名称") + private String deviceName; + + @Schema(description = "状态 1-在线 2-离线") + private String status; + + @Schema(description = "采集时间") + private LocalDateTime collectionTime; + + @Schema(description = "开始采集时间") + private String collectionStartTime; + + @Schema(description = "结束采集时间") + private String collectionEndTime; + + @Schema(description = "id集合导出用") + private String ids; + +} diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/device/vo/LineDeviceRespVO.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/device/vo/LineDeviceRespVO.java new file mode 100644 index 0000000..dde12c0 --- /dev/null +++ b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/device/vo/LineDeviceRespVO.java @@ -0,0 +1,49 @@ +package cn.iocoder.yudao.module.iot.controller.admin.device.vo; + +import cn.iocoder.yudao.framework.excel.core.annotations.DictFormat; +import com.alibaba.excel.annotation.ExcelIgnoreUnannotated; +import com.alibaba.excel.annotation.ExcelProperty; +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.Data; + + +@Schema(description = "管理后台 - 产线设备分页返回 Resq VO") +@Data +@ExcelIgnoreUnannotated +public class LineDeviceRespVO { + + @Schema(description = "ID", requiredMode = Schema.RequiredMode.REQUIRED, example = "26404") + private Long id; + + @Schema(description = "设备Id") + private Long deviceId; + + @Schema(description = "组织编码") + @ExcelProperty("组织编码") + private String lineNode; + + @Schema(description = "组织名称") + @ExcelProperty("组织名称") + private String lineName; + + @Schema(description = "设备编码") + @ExcelProperty("设备编码") + private String deviceCode; + + @Schema(description = "设备名称") + @ExcelProperty("设备名称") + private String deviceName; + + @Schema(description = "状态 1-在线 2-离线") +// @ExcelProperty(value = "连接状态", converter = DictConvert.class) + @DictFormat("iot_gateway_status") // TODO 代码优化:建议设置到对应的 DictTypeConstants 枚举类中 + private String status; + +// @Schema(description = "状态名称") +// private String statusName; + + @Schema(description = "采集时间") + @ExcelProperty("采集时间") + private String collectionTime; + +} diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/device/vo/SingleDeviceRespVO.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/device/vo/SingleDeviceRespVO.java new file mode 100644 index 0000000..92c36f8 --- /dev/null +++ b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/device/vo/SingleDeviceRespVO.java @@ -0,0 +1,19 @@ +package cn.iocoder.yudao.module.iot.controller.admin.device.vo; + + +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.Data; + +@Schema(description = "管理后台 - 单设备监控 Resp VO") +@Data +public class SingleDeviceRespVO { + + @Schema(description = "点位名称", example = "26404") + private String pointName; + + @Schema(description = "点位值", example = "26404") + private String pointValue; + + @Schema(description = "采集时间") + private String collectTime; +} diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/device/vo/device/IotDeviceAuthInfoRespVO.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/device/vo/device/IotDeviceAuthInfoRespVO.java deleted file mode 100644 index 18009bd..0000000 --- a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/device/vo/device/IotDeviceAuthInfoRespVO.java +++ /dev/null @@ -1,24 +0,0 @@ -package cn.iocoder.yudao.module.iot.controller.admin.device.vo.device; - -import io.swagger.v3.oas.annotations.media.Schema; -import lombok.Data; - -import javax.validation.constraints.NotBlank; - -@Schema(description = "管理后台 - IoT 设备认证信息 Response VO") -@Data -public class IotDeviceAuthInfoRespVO { - - @Schema(description = "客户端 ID", requiredMode = Schema.RequiredMode.REQUIRED, example = "product123.device001") - @NotBlank(message = "客户端 ID 不能为空") - private String clientId; - - @Schema(description = "用户名", requiredMode = Schema.RequiredMode.REQUIRED, example = "device001&product123") - @NotBlank(message = "用户名不能为空") - private String username; - - @Schema(description = "密码", requiredMode = Schema.RequiredMode.REQUIRED, example = "1a2b3c4d5e6f7890abcdef1234567890") - @NotBlank(message = "密码不能为空") - private String password; - -} \ No newline at end of file diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/device/vo/device/IotDeviceBindGatewayReqVO.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/device/vo/device/IotDeviceBindGatewayReqVO.java deleted file mode 100644 index d94332b..0000000 --- a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/device/vo/device/IotDeviceBindGatewayReqVO.java +++ /dev/null @@ -1,22 +0,0 @@ -package cn.iocoder.yudao.module.iot.controller.admin.device.vo.device; - -import io.swagger.v3.oas.annotations.media.Schema; -import lombok.Data; - -import javax.validation.constraints.NotEmpty; -import javax.validation.constraints.NotNull; -import java.util.Set; - -@Schema(description = "管理后台 - IoT 设备绑定网关 Request VO") -@Data -public class IotDeviceBindGatewayReqVO { - - @Schema(description = "子设备编号列表", requiredMode = Schema.RequiredMode.REQUIRED, example = "1,2,3") - @NotEmpty(message = "子设备编号列表不能为空") - private Set subIds; - - @Schema(description = "网关设备编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "10") - @NotNull(message = "网关设备编号不能为空") - private Long gatewayId; - -} diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/device/vo/device/IotDeviceByProductKeyAndNamesReqVO.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/device/vo/device/IotDeviceByProductKeyAndNamesReqVO.java deleted file mode 100644 index 5fc66f3..0000000 --- a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/device/vo/device/IotDeviceByProductKeyAndNamesReqVO.java +++ /dev/null @@ -1,22 +0,0 @@ -package cn.iocoder.yudao.module.iot.controller.admin.device.vo.device; - -import io.swagger.v3.oas.annotations.media.Schema; -import lombok.Data; - -import javax.validation.constraints.NotBlank; -import javax.validation.constraints.NotEmpty; -import java.util.List; - -@Schema(description = "管理后台 - 通过产品标识和设备名称列表获取设备 Request VO") -@Data -public class IotDeviceByProductKeyAndNamesReqVO { - - @Schema(description = "产品标识", requiredMode = Schema.RequiredMode.REQUIRED, example = "1de24640dfe") - @NotBlank(message = "产品标识不能为空") - private String productKey; - - @Schema(description = "设备名称列表", requiredMode = Schema.RequiredMode.REQUIRED, example = "device001,device002") - @NotEmpty(message = "设备名称列表不能为空") - private List deviceNames; - -} diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/device/vo/device/IotDeviceImportExcelVO.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/device/vo/device/IotDeviceImportExcelVO.java deleted file mode 100644 index a5ab444..0000000 --- a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/device/vo/device/IotDeviceImportExcelVO.java +++ /dev/null @@ -1,36 +0,0 @@ -package cn.iocoder.yudao.module.iot.controller.admin.device.vo.device; - -import cn.idev.excel.annotation.ExcelProperty; -import io.swagger.v3.oas.annotations.media.Schema; -import lombok.AllArgsConstructor; -import lombok.Builder; -import lombok.Data; -import lombok.NoArgsConstructor; - -import javax.validation.constraints.NotEmpty; - -/** - * 设备 Excel 导入 VO - */ -@Data -@Builder -@AllArgsConstructor -@NoArgsConstructor -public class IotDeviceImportExcelVO { - - @ExcelProperty("设备名称") - @NotEmpty(message = "设备名称不能为空") - private String deviceName; - - @ExcelProperty("父设备名称") - @Schema(description = "父设备名称", example = "网关001") - private String parentDeviceName; - - @ExcelProperty("产品标识") - @NotEmpty(message = "产品标识不能为空") - private String productKey; - - @ExcelProperty("设备分组") - private String groupNames; - -} diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/device/vo/device/IotDeviceImportRespVO.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/device/vo/device/IotDeviceImportRespVO.java deleted file mode 100644 index bf52b12..0000000 --- a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/device/vo/device/IotDeviceImportRespVO.java +++ /dev/null @@ -1,23 +0,0 @@ -package cn.iocoder.yudao.module.iot.controller.admin.device.vo.device; - -import io.swagger.v3.oas.annotations.media.Schema; -import lombok.Builder; -import lombok.Data; - -import java.util.List; -import java.util.Map; - -@Schema(description = "管理后台 - IoT 设备导入 Response VO") -@Data -@Builder -public class IotDeviceImportRespVO { - - @Schema(description = "创建成功的设备名称数组", requiredMode = Schema.RequiredMode.REQUIRED) - private List createDeviceNames; - - @Schema(description = "更新成功的设备名称数组", requiredMode = Schema.RequiredMode.REQUIRED) - private List updateDeviceNames; - - @Schema(description = "导入失败的设备集合,key为设备名称,value为失败原因", requiredMode = Schema.RequiredMode.REQUIRED) - private Map failureDeviceNames; -} \ No newline at end of file diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/device/vo/device/IotDevicePageReqVO.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/device/vo/device/IotDevicePageReqVO.java deleted file mode 100644 index ddedb61..0000000 --- a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/device/vo/device/IotDevicePageReqVO.java +++ /dev/null @@ -1,37 +0,0 @@ -package cn.iocoder.yudao.module.iot.controller.admin.device.vo.device; - -import cn.iocoder.yudao.framework.common.pojo.PageParam; -import cn.iocoder.yudao.framework.common.validation.InEnum; -import cn.iocoder.yudao.module.iot.core.enums.device.IotDeviceStateEnum; -import cn.iocoder.yudao.module.iot.enums.product.IotProductDeviceTypeEnum; -import io.swagger.v3.oas.annotations.media.Schema; -import lombok.Data; - -@Schema(description = "管理后台 - IoT 设备分页 Request VO") -@Data -public class IotDevicePageReqVO extends PageParam { - - @Schema(description = "设备名称", example = "王五") - private String deviceName; - - @Schema(description = "备注名称", example = "张三") - private String nickname; - - @Schema(description = "产品编号", example = "26202") - private Long productId; - - @Schema(description = "设备类型", example = "1") - @InEnum(IotProductDeviceTypeEnum.class) - private Integer deviceType; - - @Schema(description = "设备状态", example = "1") - @InEnum(IotDeviceStateEnum.class) - private Integer status; - - @Schema(description = "设备分组编号", example = "1024") - private Long groupId; - - @Schema(description = "网关设备 ID", example = "16380") - private Long gatewayId; - -} \ No newline at end of file diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/device/vo/device/IotDeviceRespVO.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/device/vo/device/IotDeviceRespVO.java deleted file mode 100644 index 0d4a9d8..0000000 --- a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/device/vo/device/IotDeviceRespVO.java +++ /dev/null @@ -1,95 +0,0 @@ -package cn.iocoder.yudao.module.iot.controller.admin.device.vo.device; - -import cn.idev.excel.annotation.ExcelIgnoreUnannotated; -import cn.idev.excel.annotation.ExcelProperty; -import cn.iocoder.yudao.framework.excel.core.annotations.DictFormat; -import cn.iocoder.yudao.framework.excel.core.convert.DictConvert; -import io.swagger.v3.oas.annotations.media.Schema; -import lombok.Data; - -import java.math.BigDecimal; -import java.time.LocalDateTime; -import java.util.Set; - -import static cn.iocoder.yudao.module.iot.enums.DictTypeConstants.DEVICE_STATE; - -@Schema(description = "管理后台 - IoT 设备 Response VO") -@Data -@ExcelIgnoreUnannotated -public class IotDeviceRespVO { - - @Schema(description = "设备编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "177") - private Long id; - - @Schema(description = "设备名称", requiredMode = Schema.RequiredMode.REQUIRED, example = "王五") - @ExcelProperty("设备名称") - private String deviceName; - - @Schema(description = "设备备注名称", example = "张三") - @ExcelProperty("设备备注名称") - private String nickname; - - @Schema(description = "设备序列号", example = "1024") - @ExcelProperty("设备序列号") - private String serialNumber; - - @Schema(description = "设备图片", example = "我是一名码农") - @ExcelProperty("设备图片") - private String picUrl; - - @Schema(description = "设备分组编号数组", example = "1,2") - private Set groupIds; - - @Schema(description = "产品编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "26202") - @ExcelProperty("产品编号") - private Long productId; - - @Schema(description = "产品名称", example = "温湿度传感器") - private String productName; // 只有部分接口返回,例如 getDeviceLocationList - - @Schema(description = "产品标识", requiredMode = Schema.RequiredMode.REQUIRED) - @ExcelProperty("产品 Key") - private String productKey; - - @Schema(description = "设备类型", requiredMode = Schema.RequiredMode.REQUIRED, example = "1") - @ExcelProperty("设备类型") - private Integer deviceType; - - @Schema(description = "网关设备 ID", example = "16380") - private Long gatewayId; - - @Schema(description = "设备状态", requiredMode = Schema.RequiredMode.REQUIRED, example = "1") - @ExcelProperty(value = "设备状态", converter = DictConvert.class) - @DictFormat(DEVICE_STATE) - private Integer state; - - @Schema(description = "最后上线时间") - @ExcelProperty("最后上线时间") - private LocalDateTime onlineTime; - - @Schema(description = "最后离线时间") - @ExcelProperty("最后离线时间") - private LocalDateTime offlineTime; - - @Schema(description = "设备激活时间") - @ExcelProperty("设备激活时间") - private LocalDateTime activeTime; - - @Schema(description = "设备密钥,用于设备认证") - @ExcelProperty("设备密钥") - private String deviceSecret; - - @Schema(description = "设备配置", example = "{\"abc\": \"efg\"}") - private String config; - - @Schema(description = "设备位置的纬度", example = "45.000000") - private BigDecimal latitude; - - @Schema(description = "设备位置的经度", example = "45.000000") - private BigDecimal longitude; - - @Schema(description = "创建时间", requiredMode = Schema.RequiredMode.REQUIRED) - @ExcelProperty("创建时间") - private LocalDateTime createTime; - -} \ No newline at end of file diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/device/vo/device/IotDeviceSaveReqVO.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/device/vo/device/IotDeviceSaveReqVO.java deleted file mode 100644 index 3195af1..0000000 --- a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/device/vo/device/IotDeviceSaveReqVO.java +++ /dev/null @@ -1,52 +0,0 @@ -package cn.iocoder.yudao.module.iot.controller.admin.device.vo.device; - -import io.swagger.v3.oas.annotations.media.Schema; -import lombok.Data; - -import javax.validation.constraints.DecimalMax; -import javax.validation.constraints.DecimalMin; -import java.math.BigDecimal; -import java.util.Set; - -@Schema(description = "管理后台 - IoT 设备新增/修改 Request VO") -@Data -public class IotDeviceSaveReqVO { - - @Schema(description = "设备编号", example = "177") - private Long id; - - @Schema(description = "设备名称", requiredMode = Schema.RequiredMode.AUTO, example = "王五") - private String deviceName; - - @Schema(description = "备注名称", example = "张三") - private String nickname; - - @Schema(description = "设备序列号", example = "123456") - private String serialNumber; - - @Schema(description = "设备图片", example = "https://iocoder.cn/1.png") - private String picUrl; - - @Schema(description = "设备分组编号数组", example = "1,2") - private Set groupIds; - - @Schema(description = "产品编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "26202") - private Long productId; - - @Schema(description = "网关设备 ID", example = "16380") - private Long gatewayId; - - @Schema(description = "设备配置", example = "{\"abc\": \"efg\"}") - private String config; - - @Schema(description = "设备位置的纬度", example = "39.915") - @DecimalMin(value = "-90", message = "纬度范围为 -90 到 90") - @DecimalMax(value = "90", message = "纬度范围为 -90 到 90") - private BigDecimal latitude; - - @Schema(description = "设备位置的经度", example = "116.404") - @DecimalMin(value = "-180", message = "经度范围为 -180 到 180") - @DecimalMax(value = "180", message = "经度范围为 -180 到 180") - private BigDecimal longitude; - -} \ No newline at end of file diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/device/vo/device/IotDeviceUnbindGatewayReqVO.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/device/vo/device/IotDeviceUnbindGatewayReqVO.java deleted file mode 100644 index a4398c6..0000000 --- a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/device/vo/device/IotDeviceUnbindGatewayReqVO.java +++ /dev/null @@ -1,22 +0,0 @@ -package cn.iocoder.yudao.module.iot.controller.admin.device.vo.device; - -import io.swagger.v3.oas.annotations.media.Schema; -import lombok.Data; - -import javax.validation.constraints.NotEmpty; -import javax.validation.constraints.NotNull; -import java.util.Set; - -@Schema(description = "管理后台 - IoT 设备解绑网关 Request VO") -@Data -public class IotDeviceUnbindGatewayReqVO { - - @Schema(description = "子设备编号列表", requiredMode = Schema.RequiredMode.REQUIRED, example = "1,2,3") - @NotEmpty(message = "子设备编号列表不能为空") - private Set subIds; - - @Schema(description = "网关设备编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "1") - @NotNull(message = "网关设备编号不能为空") - private Long gatewayId; - -} diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/device/vo/device/IotDeviceUpdateGroupReqVO.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/device/vo/device/IotDeviceUpdateGroupReqVO.java deleted file mode 100644 index e9e2529..0000000 --- a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/device/vo/device/IotDeviceUpdateGroupReqVO.java +++ /dev/null @@ -1,21 +0,0 @@ -package cn.iocoder.yudao.module.iot.controller.admin.device.vo.device; - -import io.swagger.v3.oas.annotations.media.Schema; -import lombok.Data; - -import javax.validation.constraints.NotEmpty; -import java.util.Set; - -@Schema(description = "管理后台 - IoT 设备更新分组 Request VO") -@Data -public class IotDeviceUpdateGroupReqVO { - - @Schema(description = "设备编号列表", requiredMode = Schema.RequiredMode.REQUIRED, example = "1,2,3") - @NotEmpty(message = "设备编号列表不能为空") - private Set ids; - - @Schema(description = "分组编号列表", requiredMode = Schema.RequiredMode.REQUIRED, example = "1,2,3") - @NotEmpty(message = "分组编号列表不能为空") - private Set groupIds; - -} \ No newline at end of file diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/device/vo/group/IotDeviceGroupRespVO.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/device/vo/group/IotDeviceGroupRespVO.java deleted file mode 100644 index 4fd5415..0000000 --- a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/device/vo/group/IotDeviceGroupRespVO.java +++ /dev/null @@ -1,30 +0,0 @@ -package cn.iocoder.yudao.module.iot.controller.admin.device.vo.group; - -import io.swagger.v3.oas.annotations.media.Schema; -import lombok.Data; - -import java.time.LocalDateTime; - -@Schema(description = "管理后台 - IoT 设备分组 Response VO") -@Data -public class IotDeviceGroupRespVO { - - @Schema(description = "分组 ID", requiredMode = Schema.RequiredMode.REQUIRED, example = "3583") - private Long id; - - @Schema(description = "分组名字", requiredMode = Schema.RequiredMode.REQUIRED, example = "李四") - private String name; - - @Schema(description = "分组状态", requiredMode = Schema.RequiredMode.REQUIRED, example = "1") - private Integer status; - - @Schema(description = "分组描述", example = "你说的对") - private String description; - - @Schema(description = "创建时间", requiredMode = Schema.RequiredMode.REQUIRED) - private LocalDateTime createTime; - - @Schema(description = "设备数量", requiredMode = Schema.RequiredMode.REQUIRED, example = "10") - private Long deviceCount; - -} \ No newline at end of file diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/device/vo/group/IotDeviceGroupSaveReqVO.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/device/vo/group/IotDeviceGroupSaveReqVO.java deleted file mode 100644 index aff4ce6..0000000 --- a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/device/vo/group/IotDeviceGroupSaveReqVO.java +++ /dev/null @@ -1,27 +0,0 @@ -package cn.iocoder.yudao.module.iot.controller.admin.device.vo.group; - -import io.swagger.v3.oas.annotations.media.Schema; -import lombok.Data; - -import javax.validation.constraints.NotEmpty; -import javax.validation.constraints.NotNull; - -@Schema(description = "管理后台 - IoT 设备分组新增/修改 Request VO") -@Data -public class IotDeviceGroupSaveReqVO { - - @Schema(description = "分组 ID", requiredMode = Schema.RequiredMode.REQUIRED, example = "3583") - private Long id; - - @Schema(description = "分组名字", requiredMode = Schema.RequiredMode.REQUIRED, example = "李四") - @NotEmpty(message = "分组名字不能为空") - private String name; - - @Schema(description = "分组状态", requiredMode = Schema.RequiredMode.REQUIRED, example = "1") - @NotNull(message = "分组状态不能为空") - private Integer status; - - @Schema(description = "分组描述", example = "你说的对") - private String description; - -} \ No newline at end of file diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/device/vo/message/IotDeviceMessagePageReqVO.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/device/vo/message/IotDeviceMessagePageReqVO.java deleted file mode 100644 index 07da966..0000000 --- a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/device/vo/message/IotDeviceMessagePageReqVO.java +++ /dev/null @@ -1,42 +0,0 @@ -package cn.iocoder.yudao.module.iot.controller.admin.device.vo.message; - -import cn.iocoder.yudao.framework.common.pojo.PageParam; -import cn.iocoder.yudao.framework.common.validation.InEnum; -import cn.iocoder.yudao.module.iot.core.enums.IotDeviceMessageMethodEnum; -import io.swagger.v3.oas.annotations.media.Schema; -import lombok.Data; -import org.springframework.format.annotation.DateTimeFormat; - -import javax.validation.constraints.NotNull; -import javax.validation.constraints.Size; -import java.time.LocalDateTime; - -import static cn.iocoder.yudao.framework.common.util.date.DateUtils.FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND; - -@Schema(description = "管理后台 - IoT 设备消息分页查询 Request VO") -@Data -public class IotDeviceMessagePageReqVO extends PageParam { - - @Schema(description = "设备编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "1") - @NotNull(message = "设备编号不能为空") - private Long deviceId; - - @Schema(description = "消息类型", example = "property") - @InEnum(IotDeviceMessageMethodEnum.class) - private String method; - - @Schema(description = "是否上行", example = "true") - private Boolean upstream; - - @Schema(description = "是否回复", example = "true") - private Boolean reply; - - @Schema(description = "标识符", example = "temperature") - private String identifier; - - @Schema(description = "时间范围", requiredMode = Schema.RequiredMode.NOT_REQUIRED) - @DateTimeFormat(pattern = FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND) - @Size(min = 2, max = 2, message = "请选择时间范围") - private LocalDateTime[] times; - -} \ No newline at end of file diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/device/vo/message/IotDeviceMessageRespPairVO.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/device/vo/message/IotDeviceMessageRespPairVO.java deleted file mode 100644 index 119dd02..0000000 --- a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/device/vo/message/IotDeviceMessageRespPairVO.java +++ /dev/null @@ -1,16 +0,0 @@ -package cn.iocoder.yudao.module.iot.controller.admin.device.vo.message; - -import io.swagger.v3.oas.annotations.media.Schema; -import lombok.Data; - -@Schema(description = "管理后台 - IoT 设备消息对 Response VO") -@Data -public class IotDeviceMessageRespPairVO { - - @Schema(description = "请求消息", requiredMode = Schema.RequiredMode.REQUIRED) - private IotDeviceMessageRespVO request; - - @Schema(description = "响应消息") - private IotDeviceMessageRespVO reply; // 通过 requestId 配对 - -} diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/device/vo/message/IotDeviceMessageRespVO.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/device/vo/message/IotDeviceMessageRespVO.java deleted file mode 100644 index f9e4b75..0000000 --- a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/device/vo/message/IotDeviceMessageRespVO.java +++ /dev/null @@ -1,56 +0,0 @@ -package cn.iocoder.yudao.module.iot.controller.admin.device.vo.message; - -import io.swagger.v3.oas.annotations.media.Schema; -import lombok.Data; - -import java.time.LocalDateTime; - -@Schema(description = "管理后台 - IoT 设备消息 Response VO") -@Data -public class IotDeviceMessageRespVO { - - @Schema(description = "消息编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "1024") - private String id; - - @Schema(description = "上报时间", requiredMode = Schema.RequiredMode.REQUIRED) - private LocalDateTime reportTime; - - @Schema(description = "记录时间戳", requiredMode = Schema.RequiredMode.REQUIRED) - private LocalDateTime ts; - - @Schema(description = "设备编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "123") - private Long deviceId; - - @Schema(description = "服务编号", example = "server_123") - private String serverId; - - @Schema(description = "是否上行消息", example = "true", examples = "false") - private Boolean upstream; - - @Schema(description = "是否回复消息", example = "false", examples = "true") - private Boolean reply; - - @Schema(description = "标识符", example = "temperature") - private String identifier; - - // ========== codec(编解码)字段 ========== - - @Schema(description = "请求编号", example = "req_123") - private String requestId; - - @Schema(description = "请求方法", requiredMode = Schema.RequiredMode.REQUIRED, example = "thing.property.post") - private String method; - - @Schema(description = "请求参数") - private Object params; - - @Schema(description = "响应结果") - private Object data; - - @Schema(description = "响应错误码", example = "200") - private Integer code; - - @Schema(description = "响应提示", example = "操作成功") - private String msg; - -} \ No newline at end of file diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/device/vo/message/IotDeviceMessageSendReqVO.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/device/vo/message/IotDeviceMessageSendReqVO.java deleted file mode 100644 index da88eb8..0000000 --- a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/device/vo/message/IotDeviceMessageSendReqVO.java +++ /dev/null @@ -1,27 +0,0 @@ -package cn.iocoder.yudao.module.iot.controller.admin.device.vo.message; - -import cn.iocoder.yudao.framework.common.validation.InEnum; -import cn.iocoder.yudao.module.iot.core.enums.IotDeviceMessageMethodEnum; -import io.swagger.v3.oas.annotations.media.Schema; -import lombok.Data; - -import javax.validation.constraints.NotEmpty; -import javax.validation.constraints.NotNull; - -@Schema(description = "管理后台 - IoT 设备消息发送 Request VO") // 属性上报、事件上报、状态变更等 -@Data -public class IotDeviceMessageSendReqVO { - - @Schema(description = "请求方法", requiredMode = Schema.RequiredMode.REQUIRED, example = "report") - @NotEmpty(message = "请求方法不能为空") - @InEnum(IotDeviceMessageMethodEnum.class) - private String method; - - @Schema(description = "请求参数") - private Object params; // 例如说:属性上报的 properties、事件上报的 params - - @Schema(description = "设备编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "177") - @NotNull(message = "设备编号不能为空") - private Long deviceId; - -} diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/device/vo/modbus/IotDeviceModbusConfigRespVO.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/device/vo/modbus/IotDeviceModbusConfigRespVO.java deleted file mode 100644 index ecce04d..0000000 --- a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/device/vo/modbus/IotDeviceModbusConfigRespVO.java +++ /dev/null @@ -1,48 +0,0 @@ -package cn.iocoder.yudao.module.iot.controller.admin.device.vo.modbus; - -import io.swagger.v3.oas.annotations.media.Schema; -import lombok.Data; - -import java.time.LocalDateTime; - -@Schema(description = "管理后台 - IoT 设备 Modbus 连接配置 Response VO") -@Data -public class IotDeviceModbusConfigRespVO { - - @Schema(description = "主键", requiredMode = Schema.RequiredMode.REQUIRED, example = "1") - private Long id; - - @Schema(description = "设备编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "1024") - private Long deviceId; - - @Schema(description = "设备名称", example = "温湿度传感器") - private String deviceName; - - @Schema(description = "Modbus 服务器 IP 地址", requiredMode = Schema.RequiredMode.REQUIRED, example = "192.168.1.100") - private String ip; - - @Schema(description = "Modbus 端口", requiredMode = Schema.RequiredMode.REQUIRED, example = "502") - private Integer port; - - @Schema(description = "从站地址", requiredMode = Schema.RequiredMode.REQUIRED, example = "1") - private Integer slaveId; - - @Schema(description = "连接超时时间(毫秒)", example = "3000") - private Integer timeout; - - @Schema(description = "重试间隔(毫秒)", example = "1000") - private Integer retryInterval; - - @Schema(description = "工作模式", example = "1") - private Integer mode; - - @Schema(description = "数据帧格式", example = "1") - private Integer frameFormat; - - @Schema(description = "状态", requiredMode = Schema.RequiredMode.REQUIRED, example = "0") - private Integer status; - - @Schema(description = "创建时间", requiredMode = Schema.RequiredMode.REQUIRED) - private LocalDateTime createTime; - -} diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/device/vo/modbus/IotDeviceModbusConfigSaveReqVO.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/device/vo/modbus/IotDeviceModbusConfigSaveReqVO.java deleted file mode 100644 index c44269d..0000000 --- a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/device/vo/modbus/IotDeviceModbusConfigSaveReqVO.java +++ /dev/null @@ -1,47 +0,0 @@ -package cn.iocoder.yudao.module.iot.controller.admin.device.vo.modbus; - -import cn.iocoder.yudao.framework.common.validation.InEnum; -import cn.iocoder.yudao.module.iot.core.enums.modbus.IotModbusFrameFormatEnum; -import cn.iocoder.yudao.module.iot.core.enums.modbus.IotModbusModeEnum; -import io.swagger.v3.oas.annotations.media.Schema; -import lombok.Data; - -import javax.validation.constraints.NotNull; - -@Schema(description = "管理后台 - IoT 设备 Modbus 连接配置新增/修改 Request VO") -@Data -public class IotDeviceModbusConfigSaveReqVO { - - @Schema(description = "设备编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "1024") - @NotNull(message = "设备编号不能为空") - private Long deviceId; - - @Schema(description = "Modbus 服务器 IP 地址", example = "192.168.1.100") - private String ip; - - @Schema(description = "Modbus 端口", example = "502") - private Integer port; - - @Schema(description = "从站地址", requiredMode = Schema.RequiredMode.REQUIRED, example = "1") - @NotNull(message = "从站地址不能为空") - private Integer slaveId; - - @Schema(description = "连接超时时间(毫秒)", example = "3000") - private Integer timeout; - - @Schema(description = "重试间隔(毫秒)", example = "1000") - private Integer retryInterval; - - @Schema(description = "工作模式", example = "1") - @InEnum(IotModbusModeEnum.class) - private Integer mode; - - @Schema(description = "数据帧格式", example = "1") - @InEnum(IotModbusFrameFormatEnum.class) - private Integer frameFormat; - - @Schema(description = "状态", requiredMode = Schema.RequiredMode.REQUIRED, example = "0") - @NotNull(message = "状态不能为空") - private Integer status; - -} diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/device/vo/modbus/IotDeviceModbusPointPageReqVO.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/device/vo/modbus/IotDeviceModbusPointPageReqVO.java deleted file mode 100644 index 344e8ce..0000000 --- a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/device/vo/modbus/IotDeviceModbusPointPageReqVO.java +++ /dev/null @@ -1,30 +0,0 @@ -package cn.iocoder.yudao.module.iot.controller.admin.device.vo.modbus; - -import cn.iocoder.yudao.framework.common.pojo.PageParam; -import io.swagger.v3.oas.annotations.media.Schema; -import lombok.Data; -import lombok.EqualsAndHashCode; -import lombok.ToString; - -@Schema(description = "管理后台 - IoT 设备 Modbus 点位配置分页 Request VO") -@Data -@EqualsAndHashCode(callSuper = true) -@ToString(callSuper = true) -public class IotDeviceModbusPointPageReqVO extends PageParam { - - @Schema(description = "设备编号", example = "1024") - private Long deviceId; - - @Schema(description = "属性标识符", example = "temperature") - private String identifier; - - @Schema(description = "属性名称", example = "温度") - private String name; - - @Schema(description = "Modbus 功能码", example = "3") - private Integer functionCode; - - @Schema(description = "状态", example = "0") - private Integer status; - -} diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/device/vo/modbus/IotDeviceModbusPointRespVO.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/device/vo/modbus/IotDeviceModbusPointRespVO.java deleted file mode 100644 index c590e3e..0000000 --- a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/device/vo/modbus/IotDeviceModbusPointRespVO.java +++ /dev/null @@ -1,55 +0,0 @@ -package cn.iocoder.yudao.module.iot.controller.admin.device.vo.modbus; - -import io.swagger.v3.oas.annotations.media.Schema; -import lombok.Data; - -import java.math.BigDecimal; -import java.time.LocalDateTime; - -@Schema(description = "管理后台 - IoT 设备 Modbus 点位配置 Response VO") -@Data -public class IotDeviceModbusPointRespVO { - - @Schema(description = "主键", requiredMode = Schema.RequiredMode.REQUIRED, example = "1") - private Long id; - - @Schema(description = "设备编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "1024") - private Long deviceId; - - @Schema(description = "物模型属性编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "2048") - private Long thingModelId; - - @Schema(description = "属性标识符", requiredMode = Schema.RequiredMode.REQUIRED, example = "temperature") - private String identifier; - - @Schema(description = "属性名称", requiredMode = Schema.RequiredMode.REQUIRED, example = "温度") - private String name; - - @Schema(description = "Modbus 功能码", requiredMode = Schema.RequiredMode.REQUIRED, example = "3") - private Integer functionCode; - - @Schema(description = "寄存器起始地址", requiredMode = Schema.RequiredMode.REQUIRED, example = "0") - private Integer registerAddress; - - @Schema(description = "寄存器数量", requiredMode = Schema.RequiredMode.REQUIRED, example = "1") - private Integer registerCount; - - @Schema(description = "字节序", requiredMode = Schema.RequiredMode.REQUIRED, example = "AB") - private String byteOrder; - - @Schema(description = "原始数据类型", requiredMode = Schema.RequiredMode.REQUIRED, example = "INT16") - private String rawDataType; - - @Schema(description = "缩放因子", requiredMode = Schema.RequiredMode.REQUIRED, example = "1.0") - private BigDecimal scale; - - @Schema(description = "轮询间隔(毫秒)", requiredMode = Schema.RequiredMode.REQUIRED, example = "5000") - private Integer pollInterval; - - @Schema(description = "状态", requiredMode = Schema.RequiredMode.REQUIRED, example = "0") - private Integer status; - - @Schema(description = "创建时间", requiredMode = Schema.RequiredMode.REQUIRED) - private LocalDateTime createTime; - -} diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/device/vo/modbus/IotDeviceModbusPointSaveReqVO.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/device/vo/modbus/IotDeviceModbusPointSaveReqVO.java deleted file mode 100644 index e284acc..0000000 --- a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/device/vo/modbus/IotDeviceModbusPointSaveReqVO.java +++ /dev/null @@ -1,54 +0,0 @@ -package cn.iocoder.yudao.module.iot.controller.admin.device.vo.modbus; - -import io.swagger.v3.oas.annotations.media.Schema; -import lombok.Data; - -import javax.validation.constraints.NotEmpty; -import javax.validation.constraints.NotNull; -import java.math.BigDecimal; - -@Schema(description = "管理后台 - IoT 设备 Modbus 点位配置新增/修改 Request VO") -@Data -public class IotDeviceModbusPointSaveReqVO { - - @Schema(description = "主键", example = "1") - private Long id; - - @Schema(description = "设备编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "1024") - @NotNull(message = "设备编号不能为空") - private Long deviceId; - - @Schema(description = "物模型属性编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "2048") - @NotNull(message = "物模型属性编号不能为空") - private Long thingModelId; - - @Schema(description = "Modbus 功能码", requiredMode = Schema.RequiredMode.REQUIRED, example = "3") - @NotNull(message = "Modbus 功能码不能为空") - private Integer functionCode; - - @Schema(description = "寄存器起始地址", requiredMode = Schema.RequiredMode.REQUIRED, example = "0") - @NotNull(message = "寄存器起始地址不能为空") - private Integer registerAddress; - - @Schema(description = "寄存器数量", example = "1") - private Integer registerCount; - - @Schema(description = "字节序", requiredMode = Schema.RequiredMode.REQUIRED, example = "AB") - @NotEmpty(message = "字节序不能为空") - private String byteOrder; - - @Schema(description = "原始数据类型", requiredMode = Schema.RequiredMode.REQUIRED, example = "INT16") - @NotEmpty(message = "原始数据类型不能为空") - private String rawDataType; - - @Schema(description = "缩放因子", example = "1.0") - private BigDecimal scale; - - @Schema(description = "轮询间隔(毫秒)", example = "5000") - private Integer pollInterval; - - @Schema(description = "状态", requiredMode = Schema.RequiredMode.REQUIRED, example = "0") - @NotNull(message = "状态不能为空") - private Integer status; - -} diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/device/vo/property/IotDevicePropertyDetailRespVO.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/device/vo/property/IotDevicePropertyDetailRespVO.java deleted file mode 100644 index 5771269..0000000 --- a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/device/vo/property/IotDevicePropertyDetailRespVO.java +++ /dev/null @@ -1,25 +0,0 @@ -package cn.iocoder.yudao.module.iot.controller.admin.device.vo.property; - -import cn.iocoder.yudao.module.iot.dal.dataobject.thingmodel.model.dataType.ThingModelDataSpecs; -import io.swagger.v3.oas.annotations.media.Schema; -import lombok.Data; - -import java.util.List; - -@Schema(description = "管理后台 - IoT 设备属性详细 Response VO") // 额外增加 来自 ThingModelProperty 的变量 属性 -@Data -public class IotDevicePropertyDetailRespVO extends IotDevicePropertyRespVO { - - @Schema(description = "属性名称", requiredMode = Schema.RequiredMode.REQUIRED) - private String name; - - @Schema(description = "数据类型", requiredMode = Schema.RequiredMode.REQUIRED, example = "int") - private String dataType; - - @Schema(description = "数据定义") - private ThingModelDataSpecs dataSpecs; - - @Schema(description = "数据定义列表") - private List dataSpecsList; - -} \ No newline at end of file diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/device/vo/property/IotDevicePropertyHistoryListReqVO.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/device/vo/property/IotDevicePropertyHistoryListReqVO.java deleted file mode 100644 index 3202777..0000000 --- a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/device/vo/property/IotDevicePropertyHistoryListReqVO.java +++ /dev/null @@ -1,31 +0,0 @@ -package cn.iocoder.yudao.module.iot.controller.admin.device.vo.property; - -import io.swagger.v3.oas.annotations.media.Schema; -import lombok.Data; -import org.springframework.format.annotation.DateTimeFormat; - -import javax.validation.constraints.NotEmpty; -import javax.validation.constraints.NotNull; -import javax.validation.constraints.Size; -import java.time.LocalDateTime; - -import static cn.iocoder.yudao.framework.common.util.date.DateUtils.FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND; - -@Schema(description = "管理后台 - IoT 设备属性历史列表 Request VO") -@Data -public class IotDevicePropertyHistoryListReqVO { - - @Schema(description = "设备编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "177") - @NotNull(message = "设备编号不能为空") - private Long deviceId; - - @Schema(description = "属性标识符", requiredMode = Schema.RequiredMode.REQUIRED) - @NotEmpty(message = "属性标识符不能为空") - private String identifier; - - @Schema(description = "时间范围", requiredMode = Schema.RequiredMode.NOT_REQUIRED) - @DateTimeFormat(pattern = FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND) - @Size(min = 2, max = 2, message = "请选择时间范围") - private LocalDateTime[] times; - -} \ No newline at end of file diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/device/vo/property/IotDevicePropertyRespVO.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/device/vo/property/IotDevicePropertyRespVO.java deleted file mode 100644 index 841b1f1..0000000 --- a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/device/vo/property/IotDevicePropertyRespVO.java +++ /dev/null @@ -1,19 +0,0 @@ -package cn.iocoder.yudao.module.iot.controller.admin.device.vo.property; - -import io.swagger.v3.oas.annotations.media.Schema; -import lombok.Data; - -@Schema(description = "管理后台 - IoT 设备属性 Response VO") -@Data -public class IotDevicePropertyRespVO { - - @Schema(description = "属性标识符", requiredMode = Schema.RequiredMode.REQUIRED) - private String identifier; - - @Schema(description = "属性值", requiredMode = Schema.RequiredMode.REQUIRED) - private Object value; - - @Schema(description = "更新时间", requiredMode = Schema.RequiredMode.REQUIRED) - private Long updateTime; // 由于从 TDengine 查询出来的是 Long 类型,所以这里也使用 Long 类型 - -} \ No newline at end of file diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/deviceattributetype/DeviceAttributeTypeController.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/deviceattributetype/DeviceAttributeTypeController.java new file mode 100644 index 0000000..cf0ccf8 --- /dev/null +++ b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/deviceattributetype/DeviceAttributeTypeController.java @@ -0,0 +1,110 @@ +package cn.iocoder.yudao.module.iot.controller.admin.deviceattributetype; + +import cn.iocoder.yudao.framework.apilog.core.annotation.ApiAccessLog; +import cn.iocoder.yudao.framework.common.pojo.CommonResult; +import cn.iocoder.yudao.framework.common.pojo.PageParam; +import cn.iocoder.yudao.framework.common.pojo.PageResult; +import cn.iocoder.yudao.framework.common.util.object.BeanUtils; +import cn.iocoder.yudao.framework.excel.core.util.ExcelUtils; +import cn.iocoder.yudao.module.iot.controller.admin.deviceattributetype.vo.DeviceAttributeTypePageReqVO; +import cn.iocoder.yudao.module.iot.controller.admin.deviceattributetype.vo.DeviceAttributeTypeRespVO; +import cn.iocoder.yudao.module.iot.controller.admin.deviceattributetype.vo.DeviceAttributeTypeSaveReqVO; +import cn.iocoder.yudao.module.iot.dal.dataobject.deviceattributetype.DeviceAttributeTypeDO; +import cn.iocoder.yudao.module.iot.service.deviceattributetype.DeviceAttributeTypeService; +import io.swagger.v3.oas.annotations.Operation; +import io.swagger.v3.oas.annotations.Parameter; +import io.swagger.v3.oas.annotations.tags.Tag; +import org.springframework.security.access.prepost.PreAuthorize; +import org.springframework.validation.annotation.Validated; +import org.springframework.web.bind.annotation.*; + +import javax.annotation.Resource; +import javax.servlet.http.HttpServletResponse; +import javax.validation.Valid; +import java.io.IOException; +import java.util.Arrays; +import java.util.List; +import java.util.stream.Collectors; + +import static cn.iocoder.yudao.framework.apilog.core.enums.OperateTypeEnum.EXPORT; +import static cn.iocoder.yudao.framework.common.pojo.CommonResult.success; + +@Tag(name = "管理后台 - 采集点分类") +@RestController +@RequestMapping("/iot/device-attribute-type") +@Validated +public class DeviceAttributeTypeController { + + @Resource + private DeviceAttributeTypeService deviceAttributeTypeService; + + @PostMapping("/create") + @Operation(summary = "创建采集点分类") + @PreAuthorize("@ss.hasPermission('iot:device-attribute-type:create')") + public CommonResult createDeviceAttributeType(@Valid @RequestBody DeviceAttributeTypeSaveReqVO createReqVO) { + return success(deviceAttributeTypeService.createDeviceAttributeType(createReqVO)); + } + + @PutMapping("/update") + @Operation(summary = "更新采集点分类") + @PreAuthorize("@ss.hasPermission('iot:device-attribute-type:update')") + public CommonResult updateDeviceAttributeType(@Valid @RequestBody DeviceAttributeTypeSaveReqVO updateReqVO) { + deviceAttributeTypeService.updateDeviceAttributeType(updateReqVO); + return success(true); + } + + @DeleteMapping("/delete") + @Operation(summary = "删除采集点分类") + @Parameter(name = "ids", description = "编号", required = true) + @PreAuthorize("@ss.hasPermission('iot:device-attribute-type:delete')") + public CommonResult deleteDeviceAttributeType(@RequestParam("ids") String ids) { + // 将逗号分隔的字符串转换为Long类型的List + List idList = Arrays.stream(ids.split(",")) + .map(String::trim) // 去除可能存在的空格 + .map(Long::valueOf) + .collect(Collectors.toList()); + + deviceAttributeTypeService.deleteDeviceAttributeType(idList); + return success(true); + } + + @GetMapping("/get") + @Operation(summary = "获得采集点分类") + @Parameter(name = "id", description = "编号", required = true, example = "1024") + @PreAuthorize("@ss.hasPermission('iot:device-attribute-type:query')") + public CommonResult getDeviceAttributeType(@RequestParam("id") Long id) { + DeviceAttributeTypeDO deviceAttributeType = deviceAttributeTypeService.getDeviceAttributeType(id); + return success(BeanUtils.toBean(deviceAttributeType, DeviceAttributeTypeRespVO.class)); + } + + @GetMapping("/page") + @Operation(summary = "获得采集点分类分页") + @PreAuthorize("@ss.hasPermission('iot:device-attribute-type:query')") + public CommonResult> getDeviceAttributeTypePage(@Valid DeviceAttributeTypePageReqVO pageReqVO) { + PageResult pageResult = deviceAttributeTypeService.getDeviceAttributeTypePage(pageReqVO); + return success(BeanUtils.toBean(pageResult, DeviceAttributeTypeRespVO.class)); + } + + @GetMapping("/export-excel") + @Operation(summary = "导出采集点分类 Excel") + @PreAuthorize("@ss.hasPermission('iot:device-attribute-type:export')") + @ApiAccessLog(operateType = EXPORT) + public void exportDeviceAttributeTypeExcel(@Valid DeviceAttributeTypePageReqVO pageReqVO, + HttpServletResponse response) throws IOException { + pageReqVO.setPageSize(PageParam.PAGE_SIZE_NONE); + List list = deviceAttributeTypeService.getDeviceAttributeTypePage(pageReqVO).getList(); + // 导出 Excel + ExcelUtils.write(response, "采集点分类.xls", "数据", DeviceAttributeTypeRespVO.class, + BeanUtils.toBean(list, DeviceAttributeTypeRespVO.class)); + } + + + @GetMapping("/list") + @Operation(summary = "获得采集点分类列表") + public CommonResult> getDeviceModelList() { + List deviceAttributeTypeDOList = deviceAttributeTypeService.getDeviceAttributeTypeList(); + return success(deviceAttributeTypeDOList); + + } + +} \ No newline at end of file diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/device/vo/group/IotDeviceGroupPageReqVO.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/deviceattributetype/vo/DeviceAttributeTypePageReqVO.java similarity index 50% rename from yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/device/vo/group/IotDeviceGroupPageReqVO.java rename to yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/deviceattributetype/vo/DeviceAttributeTypePageReqVO.java index 93b1a1e..723cfab 100644 --- a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/device/vo/group/IotDeviceGroupPageReqVO.java +++ b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/deviceattributetype/vo/DeviceAttributeTypePageReqVO.java @@ -1,4 +1,4 @@ -package cn.iocoder.yudao.module.iot.controller.admin.device.vo.group; +package cn.iocoder.yudao.module.iot.controller.admin.deviceattributetype.vo; import cn.iocoder.yudao.framework.common.pojo.PageParam; import io.swagger.v3.oas.annotations.media.Schema; @@ -11,15 +11,25 @@ import java.time.LocalDateTime; import static cn.iocoder.yudao.framework.common.util.date.DateUtils.FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND; -@Schema(description = "管理后台 - IoT 设备分组分页 Request VO") +@Schema(description = "管理后台 - 采集点分类分页 Request VO") @Data -public class IotDeviceGroupPageReqVO extends PageParam { +@EqualsAndHashCode(callSuper = true) +@ToString(callSuper = true) +public class DeviceAttributeTypePageReqVO extends PageParam { - @Schema(description = "分组名字", example = "李四") + @Schema(description = "分类编码") + private String code; + + @Schema(description = "分类名称", example = "张三") private String name; + @Schema(description = "备注", example = "随便") + private String remark; + @Schema(description = "创建时间") @DateTimeFormat(pattern = FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND) private LocalDateTime[] createTime; + @Schema(description = "id集合导出用") + private String ids; } \ No newline at end of file diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/deviceattributetype/vo/DeviceAttributeTypeRespVO.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/deviceattributetype/vo/DeviceAttributeTypeRespVO.java new file mode 100644 index 0000000..e71b970 --- /dev/null +++ b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/deviceattributetype/vo/DeviceAttributeTypeRespVO.java @@ -0,0 +1,40 @@ +package cn.iocoder.yudao.module.iot.controller.admin.deviceattributetype.vo; + +import com.alibaba.excel.annotation.ExcelIgnoreUnannotated; +import com.alibaba.excel.annotation.ExcelProperty; +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.Data; + +import java.time.LocalDateTime; + +@Schema(description = "管理后台 - 采集点分类 Response VO") +@Data +@ExcelIgnoreUnannotated +public class DeviceAttributeTypeRespVO { + + @Schema(description = "ID", requiredMode = Schema.RequiredMode.REQUIRED, example = "23550") + @ExcelProperty("ID") + private Long id; + + @Schema(description = "分类编码", requiredMode = Schema.RequiredMode.REQUIRED) + @ExcelProperty("分类编码") + private String code; + + @Schema(description = "分类名称", requiredMode = Schema.RequiredMode.REQUIRED, example = "张三") + @ExcelProperty("分类名称") + private String name; + + @Schema(description = "备注", example = "随便") + @ExcelProperty("备注") + private String remark; + + @Schema(description = "创建时间", requiredMode = Schema.RequiredMode.REQUIRED) + @ExcelProperty("创建时间") + private LocalDateTime createTime; + + + @Schema(description = "排序", example = "1") +// @ExcelProperty("排序") + private int sort; + +} \ No newline at end of file diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/deviceattributetype/vo/DeviceAttributeTypeSaveReqVO.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/deviceattributetype/vo/DeviceAttributeTypeSaveReqVO.java new file mode 100644 index 0000000..f94081f --- /dev/null +++ b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/deviceattributetype/vo/DeviceAttributeTypeSaveReqVO.java @@ -0,0 +1,30 @@ +package cn.iocoder.yudao.module.iot.controller.admin.deviceattributetype.vo; + +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.Data; + +import javax.validation.constraints.NotEmpty; + +@Schema(description = "管理后台 - 采集点分类新增/修改 Request VO") +@Data +public class DeviceAttributeTypeSaveReqVO { + + @Schema(description = "ID", requiredMode = Schema.RequiredMode.REQUIRED, example = "23550") + private Long id; + + @Schema(description = "分类编码", requiredMode = Schema.RequiredMode.REQUIRED) + @NotEmpty(message = "分类编码不能为空") + private String code; + + @Schema(description = "分类名称", requiredMode = Schema.RequiredMode.REQUIRED, example = "张三") + @NotEmpty(message = "分类名称不能为空") + private String name; + + @Schema(description = "备注", example = "随便") + private String remark; + + + @Schema(description = "排序",example = "2") + private int sort; + +} \ No newline at end of file diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/devicecontactmodel/DeviceContactModelController.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/devicecontactmodel/DeviceContactModelController.java new file mode 100644 index 0000000..6bd6bac --- /dev/null +++ b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/devicecontactmodel/DeviceContactModelController.java @@ -0,0 +1,111 @@ +package cn.iocoder.yudao.module.iot.controller.admin.devicecontactmodel; + +import cn.iocoder.yudao.framework.apilog.core.annotation.ApiAccessLog; +import cn.iocoder.yudao.framework.common.pojo.CommonResult; +import cn.iocoder.yudao.framework.common.pojo.PageParam; +import cn.iocoder.yudao.framework.common.pojo.PageResult; +import cn.iocoder.yudao.framework.common.util.object.BeanUtils; +import cn.iocoder.yudao.framework.excel.core.util.ExcelUtils; +import cn.iocoder.yudao.module.iot.controller.admin.devicecontactmodel.vo.*; +import cn.iocoder.yudao.module.iot.dal.dataobject.devicecontactmodel.DeviceContactModelDO; +import cn.iocoder.yudao.module.iot.service.devicecontactmodel.DeviceContactModelService; +import io.swagger.v3.oas.annotations.Operation; +import io.swagger.v3.oas.annotations.Parameter; +import io.swagger.v3.oas.annotations.tags.Tag; +import org.springframework.security.access.prepost.PreAuthorize; +import org.springframework.validation.annotation.Validated; +import org.springframework.web.bind.annotation.*; + +import javax.annotation.Resource; +import javax.servlet.http.HttpServletResponse; +import javax.validation.Valid; +import java.io.IOException; +import java.util.Arrays; +import java.util.List; +import java.util.stream.Collectors; + +import static cn.iocoder.yudao.framework.apilog.core.enums.OperateTypeEnum.EXPORT; +import static cn.iocoder.yudao.framework.common.pojo.CommonResult.success; + +@Tag(name = "管理后台 - 采集设备-模型关联") +@RestController +@RequestMapping("/iot/device-contact-model") +@Validated +public class DeviceContactModelController { + + @Resource + private DeviceContactModelService deviceContactModelService; + + @PostMapping("/create") + @Operation(summary = "创建采集设备模型-点位管理") + @PreAuthorize("@ss.hasPermission('iot:device-contact-model:create')") + public CommonResult createDeviceContactModel(@Valid @RequestBody DeviceContactModelSaveReqVO createReqVO) { + return success(deviceContactModelService.createDeviceContactModel(createReqVO)); + } + + @PutMapping("/update") + @Operation(summary = "更新采集设备模型-点位管理") + @PreAuthorize("@ss.hasPermission('iot:device-contact-model:update')") + public CommonResult updateDeviceContactModel(@Valid @RequestBody DeviceContactModelSaveReqVO updateReqVO) { + deviceContactModelService.updateDeviceContactModel(updateReqVO); + return success(true); + } + + @DeleteMapping("/delete") + @Operation(summary = "删除采集设备模型-点位管理") + @Parameter(name = "ids", description = "编号", required = true) + @PreAuthorize("@ss.hasPermission('iot:device-contact-model:delete')") + public CommonResult deleteDeviceContactModel(@RequestParam("ids") String ids) { + + // 将逗号分隔的字符串转换为Long类型的List + List idList = Arrays.stream(ids.split(",")) + .map(String::trim) // 去除可能存在的空格 + .map(Long::valueOf) + .collect(Collectors.toList()); + + deviceContactModelService.deleteDeviceContactModel(idList); + return success(true); + } + + @GetMapping("/get") + @Operation(summary = "获得采集设备模型-点位管理") + @Parameter(name = "id", description = "编号", required = true, example = "1024") + @PreAuthorize("@ss.hasPermission('iot:device-contact-model:query')") + public CommonResult getDeviceContactModel(@RequestParam("id") Long id) { + DeviceContactModelDO deviceContactModel = deviceContactModelService.getDeviceContactModel(id); + return success(BeanUtils.toBean(deviceContactModel, DeviceContactModelRespVO.class)); + } + + @GetMapping("/page") + @Operation(summary = "获得采集设备模型-点位管理分页") + @PreAuthorize("@ss.hasPermission('iot:device-contact-model:query')") + public CommonResult> getDeviceContactModelPage(@Valid DeviceContactModelPageReqVO pageReqVO) { + PageResult pageResult = deviceContactModelService.getDeviceContactModelPage(pageReqVO); + return success(BeanUtils.toBean(pageResult, DeviceContactModelRespVO.class)); + } + + + @GetMapping("/list") + @Operation(summary = "获得采集设备模型-点位管理 列表") + @Parameter(name = "id", description = "编号", required = true, example = "1024") + @PreAuthorize("@ss.hasPermission('iot:device-model-attribute:query')") + public CommonResult> getDeviceContactModelList(@RequestParam("id") Long id) { + List deviceModelAttribute = deviceContactModelService.getDeviceContactModelList(id); + return success(deviceModelAttribute); + } + + + @GetMapping("/export-excel") + @Operation(summary = "导出采集设备模型-点位管理 Excel") + @PreAuthorize("@ss.hasPermission('iot:device-contact-model:export')") + @ApiAccessLog(operateType = EXPORT) + public void exportDeviceContactModelExcel(@Valid DeviceContactModelPageReqVO pageReqVO, + HttpServletResponse response) throws IOException { + pageReqVO.setPageSize(PageParam.PAGE_SIZE_NONE); + List list = deviceContactModelService.getDeviceContactModelPage(pageReqVO).getList(); + // 导出 Excel + ExcelUtils.write(response, "采集设备模型-点位管理.xls", "数据", DeviceContactModelRespVO.class, + BeanUtils.toBean(list, DeviceContactModelRespVO.class)); + } + +} \ No newline at end of file diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/devicecontactmodel/vo/DeviceContactModelPageReqVO.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/devicecontactmodel/vo/DeviceContactModelPageReqVO.java new file mode 100644 index 0000000..71655ee --- /dev/null +++ b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/devicecontactmodel/vo/DeviceContactModelPageReqVO.java @@ -0,0 +1,57 @@ +package cn.iocoder.yudao.module.iot.controller.admin.devicecontactmodel.vo; + +import cn.iocoder.yudao.framework.common.pojo.PageParam; +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.Data; +import lombok.EqualsAndHashCode; +import lombok.ToString; +import org.springframework.format.annotation.DateTimeFormat; + +import java.time.LocalDateTime; + +import static cn.iocoder.yudao.framework.common.util.date.DateUtils.FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND; + +@Schema(description = "管理后台 - 采集设备模型-点位管理分页 Request VO") +@Data +@EqualsAndHashCode(callSuper = true) +@ToString(callSuper = true) +public class DeviceContactModelPageReqVO extends PageParam { + + @Schema(description = "id集合导出用") + private String ids; + + @Schema(description = "设备Id") + private Long deviceId; + + @Schema(description = "点位编码") + private String attributeCode; + + @Schema(description = "点位名称", example = "王五") + private String attributeName; + + @Schema(description = "点位类型", example = "1") + private String attributeType; + + @Schema(description = "数据类型", example = "1") + private String dataType; + + @Schema(description = "寄存器地址") + private String address; + + @Schema(description = "单位") + private String dataUnit; + + @Schema(description = "倍率") + private Double ratio; + + @Schema(description = "备注", example = "你说的对") + private String remark; + + @Schema(description = "采集设备模型id", example = "13862") + private Long deviceModelId; + + @Schema(description = "创建时间") + @DateTimeFormat(pattern = FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND) + private LocalDateTime[] createTime; + +} \ No newline at end of file diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/devicecontactmodel/vo/DeviceContactModelRespVO.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/devicecontactmodel/vo/DeviceContactModelRespVO.java new file mode 100644 index 0000000..e3855a7 --- /dev/null +++ b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/devicecontactmodel/vo/DeviceContactModelRespVO.java @@ -0,0 +1,66 @@ +package cn.iocoder.yudao.module.iot.controller.admin.devicecontactmodel.vo; + +import com.alibaba.excel.annotation.*; +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.Data; + +import java.time.LocalDateTime; + +@Schema(description = "管理后台 - 采集设备模型-点位管理 Response VO") +@Data +@ExcelIgnoreUnannotated +public class DeviceContactModelRespVO { + + @Schema(description = "ID", requiredMode = Schema.RequiredMode.REQUIRED, example = "13209") +// @ExcelProperty("ID") + private Long id; + + @Schema(description = "点位编码", requiredMode = Schema.RequiredMode.REQUIRED) + @ExcelProperty("点位编码") + private String attributeCode; + + @Schema(description = "点位名称", requiredMode = Schema.RequiredMode.REQUIRED, example = "王五") + @ExcelProperty("点位名称") + private String attributeName; + + @Schema(description = "点位类型", example = "1") + private String attributeType; + + @Schema(description = "数据类型", example = "1") + @ExcelProperty("数据类型") + private String dataType; + + @Schema(description = "寄存器地址") + @ExcelProperty("寄存器地址") + private String address; + + @Schema(description = "单位") + @ExcelProperty("单位") + private String dataUnit; + + @Schema(description = "倍率") + @ExcelProperty("倍率") + private Double ratio; + + @Schema(description = "备注", example = "你说的对") + @ExcelProperty("备注") + private String remark; + + @Schema(description = "采集设备模型id", requiredMode = Schema.RequiredMode.REQUIRED, example = "13862") +// @ExcelProperty("采集设备模型id") + private Long deviceModelId; + + @Schema(description = "创建时间", requiredMode = Schema.RequiredMode.REQUIRED) + @ExcelProperty("创建时间") + private LocalDateTime createTime; + + @Schema(description = "排序", example = "1") + @ExcelProperty("排序") + private int sort; + + @Schema(description = "采集点类型名称", example = "1") + @ExcelProperty("点位类型") + private String typeName; + + +} \ No newline at end of file diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/devicecontactmodel/vo/DeviceContactModelSaveReqVO.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/devicecontactmodel/vo/DeviceContactModelSaveReqVO.java new file mode 100644 index 0000000..f44b0f7 --- /dev/null +++ b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/devicecontactmodel/vo/DeviceContactModelSaveReqVO.java @@ -0,0 +1,51 @@ +package cn.iocoder.yudao.module.iot.controller.admin.devicecontactmodel.vo; + +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.Data; + +import javax.validation.constraints.NotEmpty; +import javax.validation.constraints.NotNull; + +@Schema(description = "管理后台 - 采集设备模型-点位管理新增/修改 Request VO") +@Data +public class DeviceContactModelSaveReqVO { + + @Schema(description = "ID", requiredMode = Schema.RequiredMode.REQUIRED, example = "13209") + private Long id; + + @Schema(description = "点位编码", requiredMode = Schema.RequiredMode.REQUIRED) + @NotEmpty(message = "点位编码不能为空") + private String attributeCode; + + @Schema(description = "点位名称", requiredMode = Schema.RequiredMode.REQUIRED, example = "王五") + @NotEmpty(message = "点位名称不能为空") + private String attributeName; + + @Schema(description = "点位类型", example = "1") + private String attributeType; + + @Schema(description = "数据类型", example = "1") + private String dataType; + + @Schema(description = "寄存器地址") + private String address; + + @Schema(description = "单位") + private String dataUnit; + + @Schema(description = "倍率") + private Double ratio; + + @Schema(description = "备注", example = "你说的对") + private String remark; + + @Schema(description = "采集设备模型id", requiredMode = Schema.RequiredMode.REQUIRED, example = "13862") + @NotNull(message = "采集设备模型id不能为空") + private Long deviceId; + + @Schema(description = "排序", example = "2") + private int sort; + + @Schema(description = "采集点类型名称", example = "2") + private String typeName; +} \ No newline at end of file diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/devicemodel/DeviceModelController.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/devicemodel/DeviceModelController.java new file mode 100644 index 0000000..9c43446 --- /dev/null +++ b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/devicemodel/DeviceModelController.java @@ -0,0 +1,119 @@ +package cn.iocoder.yudao.module.iot.controller.admin.devicemodel; + +import cn.iocoder.yudao.framework.apilog.core.annotation.ApiAccessLog; +import cn.iocoder.yudao.framework.common.pojo.CommonResult; +import cn.iocoder.yudao.framework.common.pojo.PageParam; +import cn.iocoder.yudao.framework.common.pojo.PageResult; +import cn.iocoder.yudao.framework.common.util.object.BeanUtils; +import cn.iocoder.yudao.framework.excel.core.util.ExcelUtils; +import cn.iocoder.yudao.module.iot.controller.admin.devicemodel.vo.DeviceModelPageReqVO; +import cn.iocoder.yudao.module.iot.controller.admin.devicemodel.vo.DeviceModelRespVO; +import cn.iocoder.yudao.module.iot.controller.admin.devicemodel.vo.DeviceModelSaveReqVO; +import cn.iocoder.yudao.module.iot.dal.dataobject.devicemodel.DeviceModelDO; +import cn.iocoder.yudao.module.iot.service.devicemodel.DeviceModelService; +import io.swagger.v3.oas.annotations.Operation; +import io.swagger.v3.oas.annotations.Parameter; +import io.swagger.v3.oas.annotations.tags.Tag; +import org.springframework.security.access.prepost.PreAuthorize; +import org.springframework.validation.annotation.Validated; +import org.springframework.web.bind.annotation.*; + +import javax.annotation.Resource; +import javax.servlet.http.HttpServletResponse; +import javax.validation.Valid; +import java.io.IOException; +import java.util.Arrays; +import java.util.List; +import java.util.stream.Collectors; + +import static cn.iocoder.yudao.framework.apilog.core.enums.OperateTypeEnum.EXPORT; +import static cn.iocoder.yudao.framework.common.pojo.CommonResult.success; + +@Tag(name = "管理后台 - 采集设备模型") +@RestController +@RequestMapping("/iot/device-model") +@Validated +public class DeviceModelController { + + @Resource + private DeviceModelService deviceModelService; + + @PostMapping("/create") + @Operation(summary = "创建采集设备模型") + @PreAuthorize("@ss.hasPermission('iot:device-model:create')") + public CommonResult createDeviceModel(@Valid @RequestBody DeviceModelSaveReqVO createReqVO) { + return success(deviceModelService.createDeviceModel(createReqVO)); + } + + @PutMapping("/update") + @Operation(summary = "更新采集设备模型") + @PreAuthorize("@ss.hasPermission('iot:device-model:update')") + public CommonResult updateDeviceModel(@Valid @RequestBody DeviceModelSaveReqVO updateReqVO) { + deviceModelService.updateDeviceModel(updateReqVO); + return success(true); + } + + @DeleteMapping("/delete") + @Operation(summary = "删除采集设备模型") + @Parameter(name = "ids", description = "编号", required = true) + @PreAuthorize("@ss.hasPermission('iot:device-model:delete')") + public CommonResult deleteDeviceModel(@RequestParam("ids") String ids) { + // 将逗号分隔的字符串转换为Long类型的List + List idList = Arrays.stream(ids.split(",")) + .map(String::trim) // 去除可能存在的空格 + .map(Long::valueOf) + .collect(Collectors.toList()); + deviceModelService.deleteDeviceModel(idList); + + return success(true); + } + + @GetMapping("/get") + @Operation(summary = "获得采集设备模型") + @Parameter(name = "id", description = "编号", required = true, example = "1024") + @PreAuthorize("@ss.hasPermission('iot:device-model:query')") + public CommonResult getDeviceModel(@RequestParam("id") Long id) { + DeviceModelDO deviceModel = deviceModelService.getDeviceModel(id); + return success(BeanUtils.toBean(deviceModel, DeviceModelRespVO.class)); + } + + @GetMapping("/page") + @Operation(summary = "获得采集设备模型分页") + @PreAuthorize("@ss.hasPermission('iot:device-model:query')") + public CommonResult> getDeviceModelPage(@Valid DeviceModelPageReqVO pageReqVO) { + PageResult pageResult = deviceModelService.getDeviceModelPage(pageReqVO); + return success(BeanUtils.toBean(pageResult, DeviceModelRespVO.class)); + } + + + @GetMapping("/export-excel") + @Operation(summary = "导出采集设备模型 Excel") + @PreAuthorize("@ss.hasPermission('iot:device-model:export')") + @ApiAccessLog(operateType = EXPORT) + public void exportDeviceModelExcel(@Valid DeviceModelPageReqVO pageReqVO, + HttpServletResponse response) throws IOException { + pageReqVO.setPageSize(PageParam.PAGE_SIZE_NONE); + List list = deviceModelService.getDeviceModelPage(pageReqVO).getList(); + // 导出 Excel + ExcelUtils.write(response, "采集设备模型.xls", "数据", DeviceModelRespVO.class, + BeanUtils.toBean(list, DeviceModelRespVO.class)); + } + + @GetMapping("/list") + @Operation(summary = "获得采集设备模型列表") + public CommonResult> getDeviceModelList() { + List deviceModelDOList = deviceModelService.getDeviceModelList(); + return success(deviceModelDOList); + + } + + + @PostMapping("/copy") + @Operation(summary = "复制设备模型") + @PreAuthorize("@ss.hasPermission('iot:device-model:create')") + public CommonResult copyDevice(@RequestParam("id") Long id) { + return success(deviceModelService.copyDevice(id)); + } + + +} \ No newline at end of file diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/rule/vo/scene/IotSceneRulePageReqVO.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/devicemodel/vo/DeviceModelPageReqVO.java similarity index 51% rename from yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/rule/vo/scene/IotSceneRulePageReqVO.java rename to yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/devicemodel/vo/DeviceModelPageReqVO.java index 8345004..7b4cb35 100644 --- a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/rule/vo/scene/IotSceneRulePageReqVO.java +++ b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/devicemodel/vo/DeviceModelPageReqVO.java @@ -1,8 +1,6 @@ -package cn.iocoder.yudao.module.iot.controller.admin.rule.vo.scene; +package cn.iocoder.yudao.module.iot.controller.admin.devicemodel.vo; -import cn.iocoder.yudao.framework.common.enums.CommonStatusEnum; import cn.iocoder.yudao.framework.common.pojo.PageParam; -import cn.iocoder.yudao.framework.common.validation.InEnum; import io.swagger.v3.oas.annotations.media.Schema; import lombok.Data; import lombok.EqualsAndHashCode; @@ -13,24 +11,29 @@ import java.time.LocalDateTime; import static cn.iocoder.yudao.framework.common.util.date.DateUtils.FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND; -@Schema(description = "管理后台 - IoT 场景联动分页 Request VO") +@Schema(description = "管理后台 - 采集设备模型分页 Request VO") @Data @EqualsAndHashCode(callSuper = true) @ToString(callSuper = true) -public class IotSceneRulePageReqVO extends PageParam { +public class DeviceModelPageReqVO extends PageParam { - @Schema(description = "场景名称", example = "赵六") + @Schema(description = "分类编码") + private String code; + + @Schema(description = "分类名称", example = "赵六") private String name; - @Schema(description = "场景描述", example = "你猜") - private String description; + @Schema(description = "通讯协议") + private String protocol; - @Schema(description = "场景状态", example = "1") - @InEnum(CommonStatusEnum.class) - private Integer status; + @Schema(description = "备注", example = "你说的对") + private String remark; @Schema(description = "创建时间") @DateTimeFormat(pattern = FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND) private LocalDateTime[] createTime; + @Schema(description = "id集合导出用") + private String ids; + } \ No newline at end of file diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/devicemodel/vo/DeviceModelRespVO.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/devicemodel/vo/DeviceModelRespVO.java new file mode 100644 index 0000000..6c18481 --- /dev/null +++ b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/devicemodel/vo/DeviceModelRespVO.java @@ -0,0 +1,41 @@ +package cn.iocoder.yudao.module.iot.controller.admin.devicemodel.vo; + +import com.alibaba.excel.annotation.ExcelIgnoreUnannotated; +import com.alibaba.excel.annotation.ExcelProperty; +import com.alibaba.excel.annotation.write.style.ColumnWidth; +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.Data; + +import java.time.LocalDateTime; + +@Schema(description = "管理后台 - 采集设备模型 Response VO") +@Data +@ExcelIgnoreUnannotated +public class DeviceModelRespVO { + + @Schema(description = "ID", requiredMode = Schema.RequiredMode.REQUIRED, example = "30028") +// @ExcelProperty("ID") + private Long id; + + @Schema(description = "模型编码", requiredMode = Schema.RequiredMode.REQUIRED) + @ExcelProperty("模型编码") + private String code; + + @Schema(description = "模型名称", requiredMode = Schema.RequiredMode.REQUIRED, example = "赵六") + @ExcelProperty("模型名称") + private String name; + + @Schema(description = "通讯协议", requiredMode = Schema.RequiredMode.REQUIRED) + @ExcelProperty("通讯协议") + private String protocol; + + @Schema(description = "备注", example = "你说的对") + @ExcelProperty("备注") + private String remark; + + @Schema(description = "创建时间", requiredMode = Schema.RequiredMode.REQUIRED) + @ExcelProperty("创建时间") + @ColumnWidth(20) // 设置此列的宽度为 20 个字符的宽度 + private LocalDateTime createTime; + +} \ No newline at end of file diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/devicemodel/vo/DeviceModelSaveReqVO.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/devicemodel/vo/DeviceModelSaveReqVO.java new file mode 100644 index 0000000..ac3750d --- /dev/null +++ b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/devicemodel/vo/DeviceModelSaveReqVO.java @@ -0,0 +1,30 @@ +package cn.iocoder.yudao.module.iot.controller.admin.devicemodel.vo; + +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.Data; + +import javax.validation.constraints.NotEmpty; + +@Schema(description = "管理后台 - 采集设备模型新增/修改 Request VO") +@Data +public class DeviceModelSaveReqVO { + + @Schema(description = "ID", requiredMode = Schema.RequiredMode.REQUIRED, example = "30028") + private Long id; + + @Schema(description = "分类编码", requiredMode = Schema.RequiredMode.REQUIRED) + @NotEmpty(message = "分类编码不能为空") + private String code; + + @Schema(description = "分类名称", requiredMode = Schema.RequiredMode.REQUIRED, example = "赵六") + @NotEmpty(message = "分类名称不能为空") + private String name; + + @Schema(description = "通讯协议", requiredMode = Schema.RequiredMode.REQUIRED) + @NotEmpty(message = "通讯协议不能为空") + private String protocol; + + @Schema(description = "备注", example = "你说的对") + private String remark; + +} \ No newline at end of file diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/devicemodelattribute/DeviceModelAttributeController.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/devicemodelattribute/DeviceModelAttributeController.java new file mode 100644 index 0000000..61136cd --- /dev/null +++ b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/devicemodelattribute/DeviceModelAttributeController.java @@ -0,0 +1,181 @@ +package cn.iocoder.yudao.module.iot.controller.admin.devicemodelattribute; + +import cn.iocoder.yudao.framework.apilog.core.annotation.ApiAccessLog; +import cn.iocoder.yudao.framework.common.pojo.CommonResult; +import cn.iocoder.yudao.framework.common.pojo.PageParam; +import cn.iocoder.yudao.framework.common.pojo.PageResult; +import cn.iocoder.yudao.framework.common.util.object.BeanUtils; +import cn.iocoder.yudao.framework.excel.core.util.ExcelUtils; +import cn.iocoder.yudao.module.iot.controller.admin.devicemodelattribute.vo.*; +import cn.iocoder.yudao.module.iot.dal.dataobject.devicemodelattribute.DeviceModelAttributeDO; +import cn.iocoder.yudao.module.iot.dal.mysql.devicemodelattribute.DeviceModelAttributeMapper; +import cn.iocoder.yudao.module.iot.service.devicemodelattribute.DeviceModelAttributeService; +import io.swagger.v3.oas.annotations.Operation; +import io.swagger.v3.oas.annotations.Parameter; +import io.swagger.v3.oas.annotations.Parameters; +import io.swagger.v3.oas.annotations.tags.Tag; +import org.springframework.security.access.prepost.PreAuthorize; +import org.springframework.validation.annotation.Validated; +import org.springframework.web.bind.annotation.*; +import org.springframework.web.multipart.MultipartFile; + +import javax.annotation.Resource; +import javax.servlet.http.HttpServletResponse; +import javax.validation.Valid; +import java.io.IOException; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; +import java.util.Map; +import java.util.stream.Collectors; + +import static cn.iocoder.yudao.framework.apilog.core.enums.OperateTypeEnum.EXPORT; +import static cn.iocoder.yudao.framework.common.pojo.CommonResult.success; + +@Tag(name = "管理后台 - 采集设备模型-点位管理") +@RestController +@RequestMapping("/iot/device-model-attribute") +@Validated +public class DeviceModelAttributeController { + + @Resource + private DeviceModelAttributeService deviceModelAttributeService; + @Resource + private DeviceModelAttributeMapper deviceModelAttributeMapper; + + @PostMapping("/create") + @Operation(summary = "创建采集设备模型-点位管理") + @PreAuthorize("@ss.hasPermission('iot:device-model-attribute:create')") + public CommonResult createDeviceModelAttribute(@Valid @RequestBody DeviceModelAttributeSaveReqVO createReqVO) { + return success(deviceModelAttributeService.createDeviceModelAttribute(createReqVO)); + } + + @PutMapping("/update") + @Operation(summary = "更新采集设备模型-点位管理") + @PreAuthorize("@ss.hasPermission('iot:device-model-attribute:update')") + public CommonResult updateDeviceModelAttribute(@Valid @RequestBody DeviceModelAttributeSaveReqVO updateReqVO) { + deviceModelAttributeService.updateDeviceModelAttribute(updateReqVO); + return success(true); + } + + @DeleteMapping("/delete") + @Operation(summary = "删除采集设备模型-点位管理") + @Parameter(name = "ids", description = "编号", required = true) + @PreAuthorize("@ss.hasPermission('iot:device-model-attribute:delete')") + public CommonResult deleteDeviceModelAttribute(@RequestParam("ids") String ids) { + // 将逗号分隔的字符串转换为Long类型的List + List idList = Arrays.stream(ids.split(",")) + .map(String::trim) // 去除可能存在的空格 + .map(Long::valueOf) + .collect(Collectors.toList()); + deviceModelAttributeService.deleteDeviceModelAttribute(idList); + return success(true); + } + + @GetMapping("/get") + @Operation(summary = "获得采集设备模型-点位管理") + @Parameter(name = "id", description = "编号", required = true, example = "1024") + @PreAuthorize("@ss.hasPermission('iot:device-model-attribute:query')") + public CommonResult getDeviceModelAttribute(@RequestParam("id") Long id) { + DeviceModelAttributeDO deviceModelAttribute = deviceModelAttributeService.getDeviceModelAttribute(id); + return success(BeanUtils.toBean(deviceModelAttribute, DeviceModelAttributeRespVO.class)); + } + + @GetMapping("/list") + @Operation(summary = "获得采集设备模型-点位管理 列表") + @Parameter(name = "id", description = "编号", required = true, example = "1024") + @PreAuthorize("@ss.hasPermission('iot:device-model-attribute:query')") + public CommonResult> getDeviceModelAttributeList(@RequestParam("id") Long id) { + List deviceModelAttribute = deviceModelAttributeService.getDeviceModelAttributeList(id); + return success(deviceModelAttribute); + } + + + @GetMapping("/page") + @Operation(summary = "获得采集设备模型-点位管理分页") + @PreAuthorize("@ss.hasPermission('iot:device-model-attribute:query')") + public CommonResult> getDeviceModelAttributePage(@Valid DeviceModelAttributePageReqVO pageReqVO) { + PageResult pageResult = deviceModelAttributeService.getDeviceModelAttributePage(pageReqVO); + return success(pageResult); + } + + @GetMapping("/export-excel") + @Operation(summary = "导出采集设备模型-点位管理 Excel") + @PreAuthorize("@ss.hasPermission('iot:device-model-attribute:export')") + @ApiAccessLog(operateType = EXPORT) + public void exportDeviceModelAttributeExcel(@Valid DeviceModelAttributePageReqVO pageReqVO, + HttpServletResponse response) throws IOException { + pageReqVO.setPageSize(PageParam.PAGE_SIZE_NONE); + List list = deviceModelAttributeService.getDeviceModelAttributePage(pageReqVO).getList(); + // 导出 Excel + ExcelUtils.write(response, "采集设备模型-点位管理.xls", "数据", DeviceModelAttributeRespVO.class,list); + } + + + @GetMapping("/operationAnalysisDetails") + @Operation(summary = "设备运行参数分析-详情") +// @Parameter(name = "deviceId,modelId", description = "设备id,点位参数id", required = true) + @PreAuthorize("@ss.hasPermission('iot:device-model-attribute:query')") + public CommonResult>> operationAnalysisDetails(@RequestParam("deviceId") Long deviceId, + @RequestParam("modelId") Long modelId, + @RequestParam(name = "collectionStartTime", required = false ) String collectionStartTime, + @RequestParam(name = "collectionEndTime", required = false ) String collectionEndTime) { + List> deviceModelAttribute = deviceModelAttributeService.operationAnalysisDetails(deviceId,modelId,collectionStartTime,collectionEndTime); + return success(deviceModelAttribute); + } + + /////////////// 以下是新增的代码 /////////////// + @GetMapping("/get-import-template") + @Operation(summary = "获取采集设备模型-点位管理导入模板") + @PreAuthorize("@ss.hasPermission('iot:device-model-attribute:import')") + @ApiAccessLog(operateType = EXPORT) + public void getImportTemplate(HttpServletResponse response) throws IOException { + // 准备模板数据 + List list = new ArrayList<>(); + list.add(DeviceModelAttributeImportExcelVO.builder() + .attributeCode("temperature_001") + .attributeName("温度") +// .attributeType("1") + .typeCode("SBCS") + .dataType("float") + .address("AI0") + .dataUnit("s") + .ratio(1.0) + .remark("温度传感器") + .build()); + list.add(DeviceModelAttributeImportExcelVO.builder() + .attributeCode("humidity_001") + .attributeName("湿度") +// .attributeType("2") + .typeCode("PROC") + .dataType("double") + .address("AI1") + .dataUnit("s") + .ratio(1.0) + .remark("湿度传感器") + .build()); + // 输出 + ExcelUtils.write(response, "采集设备模型-点位管理导入模板.xls", "数据", DeviceModelAttributeImportExcelVO.class, list); + } + + + @PostMapping("/import") + @Operation(summary = "导入设备采集点位") // 对应 User 的 "导入用户" + @Parameters({ + @Parameter(name = "file", description = "Excel 文件", required = true), + @Parameter(name = "deviceModelId", description = "设备型号ID", required = true, example = "1024"), + @Parameter(name = "updateSupport", description = "是否支持更新,默认为 false", example = "true") + }) // 完全复用你的 Parameters 注解格式 + @PreAuthorize("@ss.hasPermission('system:device-model-attribute:import')") // 对齐 User 的权限码风格 + public CommonResult importExcel( + @RequestParam("file") MultipartFile file, + @RequestParam("deviceModelId") Long deviceModelId, + @RequestParam(value = "updateSupport", required = false, defaultValue = "false") Boolean updateSupport) throws Exception { + // 1. 读取 Excel 数据 - 完全复用你的 ExcelUtils.read 调用方式 + List list = ExcelUtils.read(file, DeviceModelAttributeImportExcelVO.class); + // 2. 调用 Service 并返回结果 - 完全复用你的 success 包装方式 + return success(deviceModelAttributeService.importAttributeList(list, deviceModelId, updateSupport)); + } + + +} \ No newline at end of file diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/devicemodelattribute/vo/DeviceModelAttributeImportExcelVO.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/devicemodelattribute/vo/DeviceModelAttributeImportExcelVO.java new file mode 100644 index 0000000..12708fb --- /dev/null +++ b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/devicemodelattribute/vo/DeviceModelAttributeImportExcelVO.java @@ -0,0 +1,60 @@ +package cn.iocoder.yudao.module.iot.controller.admin.devicemodelattribute.vo; + +//import cn.iocoder.yudao.framework.excel.core.annotations.DictFormat; +//import cn.iocoder.yudao.module.system.enums.DictTypeConstants; // 沿用你项目中的字典枚举 + +import com.alibaba.excel.annotation.ExcelIgnore; +import com.alibaba.excel.annotation.ExcelProperty; +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Data; +import lombok.NoArgsConstructor; +import lombok.experimental.Accessors; + +/** + * 设备采集点位 Excel 导入 VO + * 【完全对齐 UserImportExcelVO 注解风格和结构】 + */ +@Data +@Builder +@AllArgsConstructor +@NoArgsConstructor +@Accessors(chain = false) // 完全复用你的配置,避免导入问题 +public class DeviceModelAttributeImportExcelVO { + + @ExcelProperty("点位编码") // 对应 User 的 "登录名称" + private String attributeCode; + + @ExcelProperty("点位名称") // 对应 User 的 "用户名称" + private String attributeName; + + // 点位类型,不在Excel中显示 + @ExcelIgnore + private String attributeType; + + @ExcelProperty("点位类型编码") + private String typeCode; + + @ExcelProperty(value = "数据类型") // 对应 User 的 "用户性别" +// , converter = DictConvert.class +// @DictFormat("device_data_type") // 替换为你项目中数据类型的字典编码,对齐 User 的 DictFormat 用法 + private String dataType; + + @ExcelProperty("寄存器地址") + private String address; + + @ExcelProperty("单位") // 对应 User 的 "用户邮箱" + private String dataUnit; + + @ExcelProperty("倍率") + private Double ratio; + + @ExcelProperty("备注") + private String remark; + + // 设备型号ID,不在Excel中显示,由前端传入 + @ExcelIgnore + private Long deviceModelId; + + +} \ No newline at end of file diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/devicemodelattribute/vo/DeviceModelAttributeImportRespVO.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/devicemodelattribute/vo/DeviceModelAttributeImportRespVO.java new file mode 100644 index 0000000..c4cb038 --- /dev/null +++ b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/devicemodelattribute/vo/DeviceModelAttributeImportRespVO.java @@ -0,0 +1,28 @@ +package cn.iocoder.yudao.module.iot.controller.admin.devicemodelattribute.vo; + +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.Builder; +import lombok.Data; + +import java.util.List; +import java.util.Map; + +/** + * 管理后台 - 设备采集点位导入 Response VO + * 【完全对齐 UserImportRespVO 注解风格和结构】 + */ +@Schema(description = "管理后台 - 设备采集点位导入 Response VO") +@Data +@Builder +public class DeviceModelAttributeImportRespVO { + + @Schema(description = "创建成功的点位编码数组", requiredMode = Schema.RequiredMode.REQUIRED) + private List createCodes; // 对应 User 的 createUsernames,替换为点位唯一标识(编码) + + @Schema(description = "更新成功的点位编码数组", requiredMode = Schema.RequiredMode.REQUIRED) + private List updateCodes; // 对应 User 的 updateUsernames,替换为点位唯一标识(编码) + + @Schema(description = "导入失败的点位集合,key 为点位编码,value 为失败原因", requiredMode = Schema.RequiredMode.REQUIRED) + private Map failureCodes; // 对应 User 的 failureUsernames,替换为点位唯一标识(编码) + +} \ No newline at end of file diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/devicemodelattribute/vo/DeviceModelAttributePageReqVO.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/devicemodelattribute/vo/DeviceModelAttributePageReqVO.java new file mode 100644 index 0000000..1020927 --- /dev/null +++ b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/devicemodelattribute/vo/DeviceModelAttributePageReqVO.java @@ -0,0 +1,57 @@ +package cn.iocoder.yudao.module.iot.controller.admin.devicemodelattribute.vo; + +import cn.iocoder.yudao.framework.common.pojo.PageParam; +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.Data; +import lombok.EqualsAndHashCode; +import lombok.ToString; +import org.springframework.format.annotation.DateTimeFormat; + +import java.time.LocalDateTime; + +import static cn.iocoder.yudao.framework.common.util.date.DateUtils.FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND; + +@Schema(description = "管理后台 - 采集设备模型-点位管理分页 Request VO") +@Data +@EqualsAndHashCode(callSuper = true) +@ToString(callSuper = true) +public class DeviceModelAttributePageReqVO extends PageParam { + + @Schema(description = "id") + private Long id; + + @Schema(description = "点位编码") + private String attributeCode; + + @Schema(description = "点位名称", example = "芋艿") + private String attributeName; + + @Schema(description = "点位类型", example = "1") + private String attributeType; + + @Schema(description = "数据类型", example = "2") + private String dataType; + + @Schema(description = "寄存器地址") + private String address; + + @Schema(description = "单位") + private String dataUnit; + + @Schema(description = "倍率") + private Double ratio; + + @Schema(description = "备注", example = "你说的对") + private String remark; + + @Schema(description = "采集设备模型id", example = "16848") + private Long deviceModelId; + + @Schema(description = "创建时间") + @DateTimeFormat(pattern = FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND) + private LocalDateTime[] createTime; + + @Schema(description = "id集合导出用") + private String ids; + +} \ No newline at end of file diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/devicemodelattribute/vo/DeviceModelAttributeRespVO.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/devicemodelattribute/vo/DeviceModelAttributeRespVO.java new file mode 100644 index 0000000..7ce385b --- /dev/null +++ b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/devicemodelattribute/vo/DeviceModelAttributeRespVO.java @@ -0,0 +1,73 @@ +package cn.iocoder.yudao.module.iot.controller.admin.devicemodelattribute.vo; + +import com.alibaba.excel.annotation.ExcelIgnoreUnannotated; +import com.alibaba.excel.annotation.ExcelProperty; +import com.alibaba.excel.annotation.write.style.ColumnWidth; +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.Data; + +import java.time.LocalDateTime; + +@Schema(description = "管理后台 - 采集设备模型-点位管理 Response VO") +@Data +@ExcelIgnoreUnannotated +public class DeviceModelAttributeRespVO { + + @Schema(description = "ID", requiredMode = Schema.RequiredMode.REQUIRED, example = "18518") +// @ExcelProperty("ID") + private Long id; + + @Schema(description = "点位编码", requiredMode = Schema.RequiredMode.REQUIRED) + @ExcelProperty("点位编码") + private String attributeCode; + + @Schema(description = "点位名称", requiredMode = Schema.RequiredMode.REQUIRED, example = "芋艿") + @ExcelProperty("点位名称") + private String attributeName; + + @Schema(description = "采集点位类型编码", example = "1") + @ExcelProperty("点位类型编码") + private String attributeTypeCode; + + @Schema(description = "点位类型", example = "1") +// @ExcelProperty("点位类型") + private String attributeType; + + @Schema(description = "类型名称", example = "1") +// @ExcelProperty("类型名称") + private String typeName; + + @Schema(description = "数据类型", example = "2") + @ExcelProperty("数据类型") + private String dataType; + + @Schema(description = "寄存器地址") + @ExcelProperty("寄存器地址") + private String address; + + @Schema(description = "单位") + private String dataUnit; + + @Schema(description = "单位") + @ExcelProperty("单位") + private String dataUnitName; + + @Schema(description = "倍率") + @ExcelProperty("倍率") + private Double ratio; + + @Schema(description = "备注", example = "你说的对") + @ExcelProperty("备注") + private String remark; + + @Schema(description = "采集设备模型id", requiredMode = Schema.RequiredMode.REQUIRED, example = "16848") +// @ExcelProperty("采集设备模型id") + private Long deviceModelId; + + @Schema(description = "创建时间", requiredMode = Schema.RequiredMode.REQUIRED) +// @ExcelProperty("创建时间") + @ColumnWidth(20) // 设置此列的宽度为 20 个字符的宽度 + private LocalDateTime createTime; + + +} \ No newline at end of file diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/devicemodelattribute/vo/DeviceModelAttributeSaveReqVO.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/devicemodelattribute/vo/DeviceModelAttributeSaveReqVO.java new file mode 100644 index 0000000..c44afd0 --- /dev/null +++ b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/devicemodelattribute/vo/DeviceModelAttributeSaveReqVO.java @@ -0,0 +1,48 @@ +package cn.iocoder.yudao.module.iot.controller.admin.devicemodelattribute.vo; + +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.Data; + +import javax.validation.constraints.NotEmpty; +import javax.validation.constraints.NotNull; + +@Schema(description = "管理后台 - 采集设备模型-点位管理新增/修改 Request VO") +@Data +public class DeviceModelAttributeSaveReqVO { + + @Schema(description = "ID", requiredMode = Schema.RequiredMode.REQUIRED, example = "18518") + private Long id; + + @Schema(description = "点位编码", requiredMode = Schema.RequiredMode.REQUIRED) + @NotEmpty(message = "点位编码不能为空") + private String attributeCode; + + @Schema(description = "点位名称", requiredMode = Schema.RequiredMode.REQUIRED, example = "芋艿") + @NotEmpty(message = "点位名称不能为空") + private String attributeName; + + @Schema(description = "点位类型", example = "1") + private String attributeType; + + @Schema(description = "数据类型", example = "2") + private String dataType; + + @Schema(description = "寄存器地址") + private String address; + + @Schema(description = "单位") + private String dataUnit; + + @Schema(description = "倍率") + private Double ratio; + + @Schema(description = "备注", example = "你说的对") + private String remark; + + @Schema(description = "采集设备模型id", requiredMode = Schema.RequiredMode.REQUIRED, example = "16848") + @NotNull(message = "采集设备模型id不能为空") + private Long deviceModelId; + + @Schema(description = "采集点类型名称") + private String typeName; +} \ No newline at end of file diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/devicemodelrules/DeviceModelRulesController.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/devicemodelrules/DeviceModelRulesController.java new file mode 100644 index 0000000..b3a6549 --- /dev/null +++ b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/devicemodelrules/DeviceModelRulesController.java @@ -0,0 +1,109 @@ +package cn.iocoder.yudao.module.iot.controller.admin.devicemodelrules; + +import cn.iocoder.yudao.framework.apilog.core.annotation.ApiAccessLog; +import cn.iocoder.yudao.framework.common.pojo.CommonResult; +import cn.iocoder.yudao.framework.common.pojo.PageParam; +import cn.iocoder.yudao.framework.common.pojo.PageResult; +import cn.iocoder.yudao.framework.common.util.object.BeanUtils; +import cn.iocoder.yudao.framework.excel.core.util.ExcelUtils; +import cn.iocoder.yudao.module.iot.controller.admin.devicemodelrules.vo.DeviceModelRulesPageReqVO; +import cn.iocoder.yudao.module.iot.controller.admin.devicemodelrules.vo.DeviceModelRulesRespVO; +import cn.iocoder.yudao.module.iot.controller.admin.devicemodelrules.vo.DeviceModelRulesSaveReqVO; +import cn.iocoder.yudao.module.iot.controller.admin.devicemodelrules.vo.PointRulesRespVO; +import cn.iocoder.yudao.module.iot.dal.dataobject.devicemodelrules.DeviceModelRulesDO; +import cn.iocoder.yudao.module.iot.service.devicemodelrules.DeviceModelRulesService; +import com.alibaba.excel.util.StringUtils; +import com.alibaba.fastjson.JSON; +import io.swagger.v3.oas.annotations.Operation; +import io.swagger.v3.oas.annotations.Parameter; +import io.swagger.v3.oas.annotations.tags.Tag; +import org.springframework.security.access.prepost.PreAuthorize; +import org.springframework.validation.annotation.Validated; +import org.springframework.web.bind.annotation.*; + +import javax.annotation.Resource; +import javax.servlet.http.HttpServletResponse; +import javax.validation.Valid; +import java.io.IOException; +import java.util.List; + +import static cn.iocoder.yudao.framework.apilog.core.enums.OperateTypeEnum.EXPORT; +import static cn.iocoder.yudao.framework.common.pojo.CommonResult.success; + +@Tag(name = "管理后台 - 点位规则关联模型") +@RestController +@RequestMapping("/iot/device-model-rules") +@Validated +public class DeviceModelRulesController { + + @Resource + private DeviceModelRulesService deviceModelRulesService; + + @PostMapping("/create") + @Operation(summary = "创建点位规则关联模型") + @PreAuthorize("@ss.hasPermission('iot:device-model-rules:create')") + public CommonResult createDeviceModelRules(@Valid @RequestBody DeviceModelRulesSaveReqVO createReqVO) { + return success(deviceModelRulesService.createDeviceModelRules(createReqVO)); + } + + @PutMapping("/update") + @Operation(summary = "更新点位规则关联模型") + @PreAuthorize("@ss.hasPermission('iot:device-model-rules:update')") + public CommonResult updateDeviceModelRules(@Valid @RequestBody DeviceModelRulesSaveReqVO updateReqVO) { + deviceModelRulesService.updateDeviceModelRules(updateReqVO); + return success(true); + } + + @DeleteMapping("/delete") + @Operation(summary = "删除点位规则关联模型") + @Parameter(name = "id", description = "编号", required = true) + @PreAuthorize("@ss.hasPermission('iot:device-model-rules:delete')") + public CommonResult deleteDeviceModelRules(@RequestParam("id") Long id) { + deviceModelRulesService.deleteDeviceModelRules(id); + return success(true); + } + + @GetMapping("/get") + @Operation(summary = "获得点位规则关联模型") + @Parameter(name = "id", description = "编号", required = true, example = "1024") + @PreAuthorize("@ss.hasPermission('iot:device-model-rules:query')") + public CommonResult getDeviceModelRules(@RequestParam("id") Long id) { + DeviceModelRulesDO deviceModelRules = deviceModelRulesService.getDeviceModelRules(id); + DeviceModelRulesRespVO deviceModelRulesRespVO = BeanUtils.toBean(deviceModelRules, DeviceModelRulesRespVO.class); + if (StringUtils.isNotBlank(deviceModelRulesRespVO.getFieldRule())){ + List pointRulesVOList = JSON.parseArray(deviceModelRulesRespVO.getFieldRule(), PointRulesRespVO.class); + deviceModelRulesRespVO.setPointRulesVOList(pointRulesVOList); + } + return success(deviceModelRulesRespVO); + } + + @GetMapping("/page") + @Operation(summary = "获得点位规则关联模型分页") + @PreAuthorize("@ss.hasPermission('iot:device-model-rules:query')") + public CommonResult> getDeviceModelRulesPage(@Valid DeviceModelRulesPageReqVO pageReqVO) { + PageResult pageResult = deviceModelRulesService.getDeviceModelRulesPage(pageReqVO); + PageResult modelRulesRespVOPageResult = BeanUtils.toBean(pageResult, DeviceModelRulesRespVO.class); + for (DeviceModelRulesRespVO deviceModelRulesRespVO : modelRulesRespVOPageResult.getList()) { + if (StringUtils.isNotBlank(deviceModelRulesRespVO.getFieldRule())) { + List pointRulesVOList = JSON.parseArray(deviceModelRulesRespVO.getFieldRule(), PointRulesRespVO.class); + deviceModelRulesRespVO.setPointRulesVOList(pointRulesVOList); + } + } + + return success(modelRulesRespVOPageResult); + } + + @GetMapping("/export-excel") + @Operation(summary = "导出点位规则关联模型 Excel") + @PreAuthorize("@ss.hasPermission('iot:device-model-rules:export')") + @ApiAccessLog(operateType = EXPORT) + public void exportDeviceModelRulesExcel(@Valid DeviceModelRulesPageReqVO pageReqVO, + HttpServletResponse response) throws IOException { + pageReqVO.setPageSize(PageParam.PAGE_SIZE_NONE); + List list = deviceModelRulesService.getDeviceModelRulesPage(pageReqVO).getList(); + // 导出 Excel + ExcelUtils.write(response, "点位规则关联模型.xls", "数据", DeviceModelRulesRespVO.class, + BeanUtils.toBean(list, DeviceModelRulesRespVO.class)); + } + +} \ No newline at end of file diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/devicemodelrules/vo/DeviceModelRulesPageReqVO.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/devicemodelrules/vo/DeviceModelRulesPageReqVO.java new file mode 100644 index 0000000..69bbf79 --- /dev/null +++ b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/devicemodelrules/vo/DeviceModelRulesPageReqVO.java @@ -0,0 +1,39 @@ +package cn.iocoder.yudao.module.iot.controller.admin.devicemodelrules.vo; + +import cn.iocoder.yudao.framework.common.pojo.PageParam; +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.Data; +import lombok.EqualsAndHashCode; +import lombok.ToString; +import org.springframework.format.annotation.DateTimeFormat; + +import java.time.LocalDateTime; + +import static cn.iocoder.yudao.framework.common.util.date.DateUtils.FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND; + +@Schema(description = "管理后台 - 点位规则关联模型分页 Request VO") +@Data +@EqualsAndHashCode(callSuper = true) +@ToString(callSuper = true) +public class DeviceModelRulesPageReqVO extends PageParam { + + @Schema(description = "标识符") + private String identifier; + + @Schema(description = "名称", example = "王五") + private String fieldName; + + @Schema(description = "规则") + private String fieldRule; + + @Schema(description = "默认值") + private String defaultValue; + + @Schema(description = "创建时间") + @DateTimeFormat(pattern = FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND) + private LocalDateTime[] createTime; + + @Schema(description = "关联设备模型Id", example = "13397") + private Long modelId; + +} \ No newline at end of file diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/devicemodelrules/vo/DeviceModelRulesRespVO.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/devicemodelrules/vo/DeviceModelRulesRespVO.java new file mode 100644 index 0000000..d36edfe --- /dev/null +++ b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/devicemodelrules/vo/DeviceModelRulesRespVO.java @@ -0,0 +1,50 @@ +package cn.iocoder.yudao.module.iot.controller.admin.devicemodelrules.vo; + +import com.alibaba.excel.annotation.ExcelIgnoreUnannotated; +import com.alibaba.excel.annotation.ExcelProperty; +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.Data; + +import java.time.LocalDateTime; +import java.util.List; + +@Schema(description = "管理后台 - 点位规则关联模型 Response VO") +@Data +@ExcelIgnoreUnannotated +public class DeviceModelRulesRespVO { + + @Schema(description = "ID", requiredMode = Schema.RequiredMode.REQUIRED, example = "10916") + @ExcelProperty("ID") + private Long id; + + @Schema(description = "标识符") + @ExcelProperty("标识符") + private String identifier; + + @Schema(description = "名称", example = "王五") + @ExcelProperty("名称") + private String fieldName; + + @Schema(description = "规则") + @ExcelProperty("规则") + private String fieldRule; + + @Schema(description = "默认值") + @ExcelProperty("默认值") + private String defaultValue; + + @Schema(description = "创建时间", requiredMode = Schema.RequiredMode.REQUIRED) + @ExcelProperty("创建时间") + private LocalDateTime createTime; + + @Schema(description = "关联设备模型Id", example = "13397") + @ExcelProperty("关联设备模型Id") + private Long modelId; + + @Schema(description = "点位规则") + private List pointRulesVOList; + + @Schema(description = "告警等级") + private String alarmLevel; + +} \ No newline at end of file diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/devicemodelrules/vo/DeviceModelRulesSaveReqVO.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/devicemodelrules/vo/DeviceModelRulesSaveReqVO.java new file mode 100644 index 0000000..478712a --- /dev/null +++ b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/devicemodelrules/vo/DeviceModelRulesSaveReqVO.java @@ -0,0 +1,36 @@ +package cn.iocoder.yudao.module.iot.controller.admin.devicemodelrules.vo; + +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.Data; + +import java.util.List; + +@Schema(description = "管理后台 - 点位规则关联模型新增/修改 Request VO") +@Data +public class DeviceModelRulesSaveReqVO { + + @Schema(description = "ID", requiredMode = Schema.RequiredMode.REQUIRED, example = "10916") + private Long id; + + @Schema(description = "标识符") + private String identifier; + + @Schema(description = "名称", example = "王五") + private String fieldName; + + @Schema(description = "规则") + private String fieldRule; + + @Schema(description = "默认值") + private String defaultValue; + + @Schema(description = "关联设备模型Id", example = "13397") + private Long modelId; + + @Schema(description = "告警等级") + private String alarmLevel; + + @Schema(description = "点位规则列表", implementation = PointRulesRespVO.class) + private List pointRulesVOList; + +} \ No newline at end of file diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/devicemodelrules/vo/PointRulesRespVO.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/devicemodelrules/vo/PointRulesRespVO.java new file mode 100644 index 0000000..c42cbe3 --- /dev/null +++ b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/devicemodelrules/vo/PointRulesRespVO.java @@ -0,0 +1,26 @@ +package cn.iocoder.yudao.module.iot.controller.admin.devicemodelrules.vo; + +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.Data; + +@Schema(description = "管理后台 - 点位具体规则 Resp VO") +@Data +public class PointRulesRespVO { + + @Schema(description = "点位规则") + private String rule; + + + + @Schema(description = "点位ID", requiredMode = Schema.RequiredMode.REQUIRED, example = "10916") + private String code; + + @Schema(description = "符号") + private String operator; + + @Schema(description = "符号值") + private String operatorRule; + + @Schema(description = "规则值") + private String ruleValue; +} diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/devicemodelrules/vo/RulesRespVO.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/devicemodelrules/vo/RulesRespVO.java new file mode 100644 index 0000000..ea3bed4 --- /dev/null +++ b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/devicemodelrules/vo/RulesRespVO.java @@ -0,0 +1,29 @@ +package cn.iocoder.yudao.module.iot.controller.admin.devicemodelrules.vo; + + +import io.swagger.v3.oas.annotations.media.ArraySchema; +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.Data; + +import java.util.List; + +@Schema(description = "管理后台 - 点位规则 Resp VO") +@Data +public class RulesRespVO { + + @Schema(description = "ID", requiredMode = Schema.RequiredMode.REQUIRED, example = "10916") + private Long id; + + @Schema(description = "标识符") + private String identifier; + + @Schema(description = "名称") + private String fieldName; + + @Schema(description = "默认值") + private String defaultValue; + + @ArraySchema(schema = @Schema(implementation = PointRulesRespVO.class)) + private List pointRulesVOList; + +} diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/deviceoperationrecord/DeviceOperationRecordController.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/deviceoperationrecord/DeviceOperationRecordController.java new file mode 100644 index 0000000..63aba03 --- /dev/null +++ b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/deviceoperationrecord/DeviceOperationRecordController.java @@ -0,0 +1,129 @@ +package cn.iocoder.yudao.module.iot.controller.admin.deviceoperationrecord; + +import cn.iocoder.yudao.framework.apilog.core.annotation.ApiAccessLog; +import cn.iocoder.yudao.framework.common.pojo.CommonResult; +import cn.iocoder.yudao.framework.common.pojo.PageParam; +import cn.iocoder.yudao.framework.common.pojo.PageResult; +import cn.iocoder.yudao.framework.common.util.object.BeanUtils; +import cn.iocoder.yudao.framework.excel.core.util.ExcelUtils; +import cn.iocoder.yudao.module.iot.controller.admin.deviceoperationrecord.vo.*; +import cn.iocoder.yudao.module.iot.dal.dataobject.deviceoperationrecord.DeviceOperationRecordDO; +import cn.iocoder.yudao.module.iot.service.deviceoperationrecord.DeviceOperationRecordService; +import io.swagger.v3.oas.annotations.Operation; +import io.swagger.v3.oas.annotations.Parameter; +import io.swagger.v3.oas.annotations.tags.Tag; +import org.springframework.security.access.prepost.PreAuthorize; +import org.springframework.validation.annotation.Validated; +import org.springframework.web.bind.annotation.*; + +import javax.annotation.Resource; +import javax.servlet.http.HttpServletResponse; +import javax.validation.Valid; +import java.io.IOException; +import java.net.URLEncoder; +import java.time.LocalDateTime; +import java.time.format.DateTimeFormatter; +import java.util.List; + +import static cn.iocoder.yudao.framework.apilog.core.enums.OperateTypeEnum.EXPORT; +import static cn.iocoder.yudao.framework.common.pojo.CommonResult.success; + +@Tag(name = "管理后台 - 运行记录") +@RestController +@RequestMapping("/iot/device-operation-record") +@Validated +public class DeviceOperationRecordController { + + @Resource + private DeviceOperationRecordService deviceOperationRecordService; + + @PostMapping("/create") + @Operation(summary = "创建运行记录") + @PreAuthorize("@ss.hasPermission('iot:device-operation-record:create')") + public CommonResult createDeviceOperationRecord(@Valid @RequestBody DeviceOperationRecordSaveReqVO createReqVO) { + return success(deviceOperationRecordService.createDeviceOperationRecord(createReqVO)); + } + + @PutMapping("/update") + @Operation(summary = "更新运行记录") + @PreAuthorize("@ss.hasPermission('iot:device-operation-record:update')") + public CommonResult updateDeviceOperationRecord(@Valid @RequestBody DeviceOperationRecordSaveReqVO updateReqVO) { + deviceOperationRecordService.updateDeviceOperationRecord(updateReqVO); + return success(true); + } + + @DeleteMapping("/delete") + @Operation(summary = "删除运行记录") + @Parameter(name = "id", description = "编号", required = true) + @PreAuthorize("@ss.hasPermission('iot:device-operation-record:delete')") + public CommonResult deleteDeviceOperationRecord(@RequestParam("id") Long id) { + deviceOperationRecordService.deleteDeviceOperationRecord(id); + return success(true); + } + + @GetMapping("/get") + @Operation(summary = "获得运行记录") + @Parameter(name = "id", description = "编号", required = true, example = "1024") + @PreAuthorize("@ss.hasPermission('iot:device-operation-record:query')") + public CommonResult getDeviceOperationRecord(@RequestParam("id") Long id) { + DeviceOperationRecordDO deviceOperationRecord = deviceOperationRecordService.getDeviceOperationRecord(id); + return success(BeanUtils.toBean(deviceOperationRecord, DeviceOperationRecordRespVO.class)); + } + + @GetMapping("/page") + @Operation(summary = "获得运行记录分页") + @PreAuthorize("@ss.hasPermission('iot:device-operation-record:query')") + public CommonResult> getDeviceOperationRecordPage(@Valid DeviceOperationRecordPageReqVO pageReqVO) { + PageResult pageResult = deviceOperationRecordService.getDeviceOperationRecordPage(pageReqVO); + return success(BeanUtils.toBean(pageResult, DeviceOperationRecordRespVO.class)); + } + + @GetMapping("/export-excel") + @Operation(summary = "导出运行记录 Excel") + @PreAuthorize("@ss.hasPermission('iot:device-operation-record:export')") + @ApiAccessLog(operateType = EXPORT) + public void exportDeviceOperationRecordExcel(@Valid DeviceOperationRecordPageReqVO pageReqVO, + HttpServletResponse response) throws IOException { + pageReqVO.setPageSize(PageParam.PAGE_SIZE_NONE); + List list = deviceOperationRecordService.getDeviceOperationRecordPage(pageReqVO).getList(); + // 导出 Excel + ExcelUtils.write(response, "运行记录.xls", "数据", DeviceOperationRecordRespVO.class, + BeanUtils.toBean(list, DeviceOperationRecordRespVO.class)); + } + + @GetMapping("/deviceOperationPage") + @Operation(summary = "设备运行报表") + @PreAuthorize("@ss.hasPermission('iot:device-operation-record:query')") + public CommonResult> deviceOperationPage(@Valid DeviceTotalTimeRecordReqVO pageReqVO) { + PageResult pageResult = deviceOperationRecordService.deviceOperationPage(pageReqVO); + return success(pageResult); + } + + + @GetMapping("/deviceOperationList") + @Operation(summary = "产线设备运行开机率/稼动率-大屏") + @PreAuthorize("@ss.hasPermission('iot:device-operation-record:query')") + public CommonResult> deviceOperationList(@Valid DeviceTotalTimeRecordReqVO pageReqVO) { + List deviceTotalTimeRecordRespVOList = deviceOperationRecordService.deviceOperationList(pageReqVO); + return success(deviceTotalTimeRecordRespVOList); + } + + + @GetMapping("/export-device-operation-report") + @Operation(summary = "导出设备运行报表记录 Excel") + @PreAuthorize("@ss.hasPermission('iot:device-operation-record:export')") + @ApiAccessLog(operateType = EXPORT) + public void exportDeviceOperationReport(@Valid DeviceTotalTimeRecordReqVO pageReqVO, + HttpServletResponse response) throws IOException { + PageResult pageResult = deviceOperationRecordService.deviceOperationPage(pageReqVO); + // 设置响应头 + response.setContentType("application/vnd.ms-excel;charset=UTF-8"); + response.setHeader("Content-Disposition", + "attachment;filename=" + URLEncoder.encode("设备运行报表记录.xls", "UTF-8")); + response.setHeader("Content-Encoding", "identity"); + // 导出Excel + String fileName = String.format("设备运行报表记录_%s.xls", LocalDateTime.now().format(DateTimeFormatter.ofPattern("yyyyMMddHHmmss"))); + // 导出 Excel + ExcelUtils.write(response, fileName, "数据", DeviceTotalTimeRecordRespVO.class,pageResult.getList()); + } +} \ No newline at end of file diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/deviceoperationrecord/utils/TimeConverterUtil.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/deviceoperationrecord/utils/TimeConverterUtil.java new file mode 100644 index 0000000..1a5e8b0 --- /dev/null +++ b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/deviceoperationrecord/utils/TimeConverterUtil.java @@ -0,0 +1,63 @@ +package cn.iocoder.yudao.module.iot.controller.admin.deviceoperationrecord.utils; + +import org.springframework.stereotype.Component; + +@Component +public class TimeConverterUtil { + + private static final int SECONDS_PER_HOUR = 3600; + + /** + * 秒转小时(保留2位小数) + */ + public static double secondsToHours(double seconds) { + if (seconds <= 0) { + return 0.0; + } + double hours = seconds / SECONDS_PER_HOUR; + return formatDouble(hours, 2); + } + + /** + * 秒转小时(自定义小数位数) + */ + public static double secondsToHours(double seconds, int scale) { + if (seconds <= 0) { + return 0.0; + } + double hours = seconds / SECONDS_PER_HOUR; + return formatDouble(hours, scale); + } + + /** + * 计算稼动率 + * 稼动率 = 运行时间 / (运行时间 + 待机时间 + 故障时间 + 警告时间) * 100% + */ + public static double calculateUtilizationRate(double runningTime, double standbyTime, + double faultTime, double warningTime) { + double totalTime = runningTime + standbyTime + faultTime + warningTime; + if (totalTime <= 0) { + return 0.0; + } + double rate = runningTime / totalTime * 100; + return formatDouble(rate, 2); + } + + /** + * 格式化小数 + */ + public static double formatDouble(double value, int scale) { + if (scale < 0) { + scale = 2; + } + double factor = Math.pow(10, scale); + return Math.round(value * factor) / factor; + } + + /** + * 获取百分比字符串 + */ + public static String getPercentString(double value) { + return formatDouble(value * 100, 2) + "%"; + } +} \ No newline at end of file diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/deviceoperationrecord/vo/DeviceOperationRecordPageReqVO.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/deviceoperationrecord/vo/DeviceOperationRecordPageReqVO.java new file mode 100644 index 0000000..03ef5ad --- /dev/null +++ b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/deviceoperationrecord/vo/DeviceOperationRecordPageReqVO.java @@ -0,0 +1,39 @@ +package cn.iocoder.yudao.module.iot.controller.admin.deviceoperationrecord.vo; + +import cn.iocoder.yudao.framework.common.pojo.PageParam; +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.Data; +import lombok.EqualsAndHashCode; +import lombok.ToString; +import org.springframework.format.annotation.DateTimeFormat; + +import java.time.LocalDateTime; + +import static cn.iocoder.yudao.framework.common.util.date.DateUtils.FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND; + +@Schema(description = "管理后台 - 运行记录分页 Request VO") +@Data +@EqualsAndHashCode(callSuper = true) +@ToString(callSuper = true) +public class DeviceOperationRecordPageReqVO extends PageParam { + + @Schema(description = "设备ID", example = "18965") + private Long deviceId; + + @Schema(description = "模型ID", example = "26876") + private Long modelId; + + @Schema(description = "规则值") + private String rule; + + @Schema(description = "地址值") + private String addressValue; + + @Schema(description = "创建时间") + @DateTimeFormat(pattern = FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND) + private LocalDateTime[] createTime; + + @Schema(description = "类型 1-RUNNING 2-ALRAM", example = "2") + private Integer recordType; + +} \ No newline at end of file diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/deviceoperationrecord/vo/DeviceOperationRecordRespVO.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/deviceoperationrecord/vo/DeviceOperationRecordRespVO.java new file mode 100644 index 0000000..7b5eecc --- /dev/null +++ b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/deviceoperationrecord/vo/DeviceOperationRecordRespVO.java @@ -0,0 +1,43 @@ +package cn.iocoder.yudao.module.iot.controller.admin.deviceoperationrecord.vo; + +import com.alibaba.excel.annotation.ExcelIgnoreUnannotated; +import com.alibaba.excel.annotation.ExcelProperty; +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.Data; + +import java.time.LocalDateTime; + +@Schema(description = "管理后台 - 运行记录 Response VO") +@Data +@ExcelIgnoreUnannotated +public class DeviceOperationRecordRespVO { + + @Schema(description = "ID", requiredMode = Schema.RequiredMode.REQUIRED, example = "4283") + @ExcelProperty("ID") + private Long id; + + @Schema(description = "设备ID", example = "18965") + @ExcelProperty("设备ID") + private Long deviceId; + + @Schema(description = "模型ID", example = "26876") + @ExcelProperty("模型ID") + private Long modelId; + + @Schema(description = "规则值") + @ExcelProperty("规则值") + private String rule; + + @Schema(description = "地址值") + @ExcelProperty("地址值") + private String addressValue; + + @Schema(description = "创建时间", requiredMode = Schema.RequiredMode.REQUIRED) + @ExcelProperty("创建时间") + private LocalDateTime createTime; + + @Schema(description = "类型 1-RUNNING 2-ALRAM", example = "2") + @ExcelProperty("类型 1-RUNNING 2-ALRAM") + private Integer recordType; + +} \ No newline at end of file diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/deviceoperationrecord/vo/DeviceOperationRecordSaveReqVO.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/deviceoperationrecord/vo/DeviceOperationRecordSaveReqVO.java new file mode 100644 index 0000000..86dc71c --- /dev/null +++ b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/deviceoperationrecord/vo/DeviceOperationRecordSaveReqVO.java @@ -0,0 +1,28 @@ +package cn.iocoder.yudao.module.iot.controller.admin.deviceoperationrecord.vo; + +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.Data; + +@Schema(description = "管理后台 - 运行记录新增/修改 Request VO") +@Data +public class DeviceOperationRecordSaveReqVO { + + @Schema(description = "ID", requiredMode = Schema.RequiredMode.REQUIRED, example = "4283") + private Long id; + + @Schema(description = "设备ID", example = "18965") + private Long deviceId; + + @Schema(description = "模型ID", example = "26876") + private Long modelId; + + @Schema(description = "规则值") + private String rule; + + @Schema(description = "地址值") + private String addressValue; + + @Schema(description = "类型 1-RUNNING 2-ALRAM", example = "2") + private Integer recordType; + +} \ No newline at end of file diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/deviceoperationrecord/vo/DeviceTotalTimeRecordReqVO.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/deviceoperationrecord/vo/DeviceTotalTimeRecordReqVO.java new file mode 100644 index 0000000..5e97d36 --- /dev/null +++ b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/deviceoperationrecord/vo/DeviceTotalTimeRecordReqVO.java @@ -0,0 +1,31 @@ +package cn.iocoder.yudao.module.iot.controller.admin.deviceoperationrecord.vo; + + +import cn.iocoder.yudao.framework.common.pojo.PageParam; +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.Data; +import lombok.EqualsAndHashCode; +import lombok.ToString; + +@Schema(description = "管理后台 - 运行记录分页 Request VO") +@Data +@EqualsAndHashCode(callSuper = true) +@ToString(callSuper = true) +public class DeviceTotalTimeRecordReqVO extends PageParam{ + + @Schema(description = "设备编码") + private String deviceCode; + + @Schema(description = "设备名称") + private String deviceName; + + @Schema(description = "开始时间") + private String startTime; + + @Schema(description = "结束时间") + private String endTime; + + @Schema(description = "ids导出集合用") + private String ids; + +} diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/deviceoperationrecord/vo/DeviceTotalTimeRecordRespVO.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/deviceoperationrecord/vo/DeviceTotalTimeRecordRespVO.java new file mode 100644 index 0000000..5b4d882 --- /dev/null +++ b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/deviceoperationrecord/vo/DeviceTotalTimeRecordRespVO.java @@ -0,0 +1,71 @@ +package cn.iocoder.yudao.module.iot.controller.admin.deviceoperationrecord.vo; + +import com.alibaba.excel.annotation.ExcelIgnoreUnannotated; +import com.alibaba.excel.annotation.ExcelProperty; +import com.fasterxml.jackson.annotation.JsonFormat; +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.Data; + +@Schema(description = "管理后台 - 运行设备报表时间记录 Request VO") +@Data +@ExcelIgnoreUnannotated +public class DeviceTotalTimeRecordRespVO { + + @Schema(description = "ID", requiredMode = Schema.RequiredMode.REQUIRED, example = "4283") + private Long id; + + @Schema(description = "设备编码") + @ExcelProperty("设备编码") + private String deviceCode; + + @Schema(description = "设备名称") + @ExcelProperty("设备名称") + private String deviceName; + + @Schema(description = "离线时间(小时)") +// @ExcelProperty("离线时间(s)") + private double totalOfflineTime; + + @Schema(description = "运行时间(s)") + @ExcelProperty("运行时间(s)") + private double totalRunningTime; + + @Schema(description = "待机时间(s)") + @ExcelProperty("待机时间(s)") + private double totalStandbyTime; + + @Schema(description = "故障时间(s)") + @ExcelProperty("故障时间(s)") + private double totalFaultTime; + + @Schema(description = "警告时间(s)") +// @ExcelProperty("警告时间(s)") + private double totalWarningTime; + + @Schema(description = "开机率") + @ExcelProperty("开机率") + private String powerOnRate; + + @Schema(description = "稼动率") + @ExcelProperty("稼动率") + private String utilizationRate; + + @Schema(description = "开始时间") + @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss") + @ExcelProperty("开始时间") + private String startTime; + + @Schema(description = "结束时间") + @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss") + @ExcelProperty("结束时间") + private String endTime; + + @Schema(description = "产线编码") + private String lineCode; + + @Schema(description = "产线名称") + private String lineName; + + @Schema(description = "产线id") + private Long lineId; +} diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/devicepointrules/DevicePointRulesController.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/devicepointrules/DevicePointRulesController.java new file mode 100644 index 0000000..a0efa0b --- /dev/null +++ b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/devicepointrules/DevicePointRulesController.java @@ -0,0 +1,114 @@ +package cn.iocoder.yudao.module.iot.controller.admin.devicepointrules; + +import cn.iocoder.yudao.framework.apilog.core.annotation.ApiAccessLog; +import cn.iocoder.yudao.framework.common.pojo.CommonResult; +import cn.iocoder.yudao.framework.common.pojo.PageParam; +import cn.iocoder.yudao.framework.common.pojo.PageResult; +import cn.iocoder.yudao.framework.common.util.object.BeanUtils; +import cn.iocoder.yudao.framework.excel.core.util.ExcelUtils; +import cn.iocoder.yudao.module.iot.controller.admin.devicemodelrules.vo.PointRulesRespVO; +import cn.iocoder.yudao.module.iot.controller.admin.devicepointrules.vo.DevicePointRulesPageReqVO; +import cn.iocoder.yudao.module.iot.controller.admin.devicepointrules.vo.DevicePointRulesRespVO; +import cn.iocoder.yudao.module.iot.controller.admin.devicepointrules.vo.DevicePointRulesSaveReqVO; +import cn.iocoder.yudao.module.iot.dal.dataobject.devicepointrules.DevicePointRulesDO; +import cn.iocoder.yudao.module.iot.service.devicepointrules.DevicePointRulesService; +import com.alibaba.excel.util.StringUtils; +import com.alibaba.fastjson.JSON; +import io.swagger.v3.oas.annotations.Operation; +import io.swagger.v3.oas.annotations.Parameter; +import io.swagger.v3.oas.annotations.tags.Tag; +import org.springframework.security.access.prepost.PreAuthorize; +import org.springframework.validation.annotation.Validated; +import org.springframework.web.bind.annotation.*; + +import javax.annotation.Resource; +import javax.servlet.http.HttpServletResponse; +import javax.validation.Valid; +import java.io.IOException; +import java.util.List; + +import static cn.iocoder.yudao.framework.apilog.core.enums.OperateTypeEnum.EXPORT; +import static cn.iocoder.yudao.framework.common.pojo.CommonResult.success; + +@Tag(name = "管理后台 - 点位规则") +@RestController +@RequestMapping("/iot/device-point-rules") +@Validated +public class DevicePointRulesController { + + @Resource + private DevicePointRulesService devicePointRulesService; + + @PostMapping("/create") + @Operation(summary = "创建点位规则") + @PreAuthorize("@ss.hasPermission('iot:device-point-rules:create')") + public CommonResult createDevicePointRules(@Valid @RequestBody DevicePointRulesSaveReqVO createReqVO) { + return success(devicePointRulesService.createDevicePointRules(createReqVO)); + } + + @PutMapping("/update") + @Operation(summary = "更新点位规则") + @PreAuthorize("@ss.hasPermission('iot:device-point-rules:update')") + public CommonResult updateDevicePointRules(@Valid @RequestBody DevicePointRulesSaveReqVO updateReqVO) { + devicePointRulesService.updateDevicePointRules(updateReqVO); + return success(true); + } + + @DeleteMapping("/delete") + @Operation(summary = "删除点位规则") + @Parameter(name = "id", description = "编号", required = true) + @PreAuthorize("@ss.hasPermission('iot:device-point-rules:delete')") + public CommonResult deleteDevicePointRules(@RequestParam("id") Long id) { + devicePointRulesService.deleteDevicePointRules(id); + return success(true); + } + + @GetMapping("/get") + @Operation(summary = "获得点位规则") + @Parameter(name = "id", description = "编号", required = true, example = "1024") + @PreAuthorize("@ss.hasPermission('iot:device-point-rules:query')") + public CommonResult getDevicePointRules(@RequestParam("id") Long id) { + DevicePointRulesDO devicePointRules = devicePointRulesService.getDevicePointRules(id); + return success(BeanUtils.toBean(devicePointRules, DevicePointRulesRespVO.class)); + } + + @GetMapping("/page") + @Operation(summary = "获得点位规则分页") + @PreAuthorize("@ss.hasPermission('iot:device-point-rules:query')") + public CommonResult> getDevicePointRulesPage(@Valid DevicePointRulesPageReqVO pageReqVO) { + PageResult pageResult = devicePointRulesService.getDevicePointRulesPage(pageReqVO); + PageResult rulesRespVOPageResult = BeanUtils.toBean(pageResult, DevicePointRulesRespVO.class); + for (DevicePointRulesRespVO devicePointRulesRespVO : rulesRespVOPageResult.getList()) { + if (StringUtils.isNotBlank(devicePointRulesRespVO.getFieldRule())) { + List pointRulesVOList = JSON.parseArray(devicePointRulesRespVO.getFieldRule(), PointRulesRespVO.class); + devicePointRulesRespVO.setPointRulesVOList(pointRulesVOList); + } + + } + + return success(rulesRespVOPageResult); + } + + @GetMapping("/export-excel") + @Operation(summary = "导出点位规则 Excel") + @PreAuthorize("@ss.hasPermission('iot:device-point-rules:export')") + @ApiAccessLog(operateType = EXPORT) + public void exportDevicePointRulesExcel(@Valid DevicePointRulesPageReqVO pageReqVO, + HttpServletResponse response) throws IOException { + pageReqVO.setPageSize(PageParam.PAGE_SIZE_NONE); + List list = devicePointRulesService.getDevicePointRulesPage(pageReqVO).getList(); + // 导出 Excel + ExcelUtils.write(response, "点位规则.xls", "数据", DevicePointRulesRespVO.class, + BeanUtils.toBean(list, DevicePointRulesRespVO.class)); + } + + @GetMapping("/getList") + @Operation(summary = "获得点位规则列表") + @Parameter(name = "id", description = "编号", required = true, example = "1024") + @PreAuthorize("@ss.hasPermission('iot:device-point-rules:query')") + public CommonResult> getList(@RequestParam("id") Long id) { + List devicePointRules = devicePointRulesService.getList(id); + return success(devicePointRules); + } + +} \ No newline at end of file diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/devicepointrules/vo/DevicePointRulesPageReqVO.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/devicepointrules/vo/DevicePointRulesPageReqVO.java new file mode 100644 index 0000000..4a5d1c4 --- /dev/null +++ b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/devicepointrules/vo/DevicePointRulesPageReqVO.java @@ -0,0 +1,39 @@ +package cn.iocoder.yudao.module.iot.controller.admin.devicepointrules.vo; + +import cn.iocoder.yudao.framework.common.pojo.PageParam; +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.Data; +import lombok.EqualsAndHashCode; +import lombok.ToString; +import org.springframework.format.annotation.DateTimeFormat; + +import java.time.LocalDateTime; + +import static cn.iocoder.yudao.framework.common.util.date.DateUtils.FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND; + +@Schema(description = "管理后台 - 点位规则分页 Request VO") +@Data +@EqualsAndHashCode(callSuper = true) +@ToString(callSuper = true) +public class DevicePointRulesPageReqVO extends PageParam { + + @Schema(description = "标识符") + private String identifier; + + @Schema(description = "名称", example = "赵六") + private String fieldName; + + @Schema(description = "规则") + private String fieldRule; + + @Schema(description = "默认值") + private String defaultValue; + + @Schema(description = "创建时间") + @DateTimeFormat(pattern = FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND) + private LocalDateTime[] createTime; + + @Schema(description = "设备模型Id", example = "19582") + private Long deviceId; + +} \ No newline at end of file diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/devicepointrules/vo/DevicePointRulesRespVO.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/devicepointrules/vo/DevicePointRulesRespVO.java new file mode 100644 index 0000000..382c4db --- /dev/null +++ b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/devicepointrules/vo/DevicePointRulesRespVO.java @@ -0,0 +1,51 @@ +package cn.iocoder.yudao.module.iot.controller.admin.devicepointrules.vo; + +import cn.iocoder.yudao.module.iot.controller.admin.devicemodelrules.vo.PointRulesRespVO; +import com.alibaba.excel.annotation.ExcelIgnoreUnannotated; +import com.alibaba.excel.annotation.ExcelProperty; +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.Data; + +import java.time.LocalDateTime; +import java.util.List; + +@Schema(description = "管理后台 - 点位规则 Response VO") +@Data +@ExcelIgnoreUnannotated +public class DevicePointRulesRespVO { + + @Schema(description = "ID", requiredMode = Schema.RequiredMode.REQUIRED, example = "13120") + @ExcelProperty("ID") + private Long id; + + @Schema(description = "标识符") + @ExcelProperty("标识符") + private String identifier; + + @Schema(description = "名称", example = "赵六") + @ExcelProperty("名称") + private String fieldName; + + @Schema(description = "规则") + @ExcelProperty("规则") + private String fieldRule; + + @Schema(description = "默认值") + @ExcelProperty("默认值") + private String defaultValue; + + @Schema(description = "创建时间", requiredMode = Schema.RequiredMode.REQUIRED) + @ExcelProperty("创建时间") + private LocalDateTime createTime; + + @Schema(description = "设备模型Id", example = "19582") + @ExcelProperty("设备模型Id") + private Long deviceId; + + @Schema(description = "点位规则") + private List pointRulesVOList; + + @Schema(description = "告警等级", example = "19582") + private String alarmLevel; + +} \ No newline at end of file diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/devicepointrules/vo/DevicePointRulesSaveReqVO.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/devicepointrules/vo/DevicePointRulesSaveReqVO.java new file mode 100644 index 0000000..a927a2e --- /dev/null +++ b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/devicepointrules/vo/DevicePointRulesSaveReqVO.java @@ -0,0 +1,36 @@ +package cn.iocoder.yudao.module.iot.controller.admin.devicepointrules.vo; + +import cn.iocoder.yudao.module.iot.controller.admin.devicemodelrules.vo.PointRulesRespVO; +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.Data; + +import java.util.List; + +@Schema(description = "管理后台 - 点位规则新增/修改 Request VO") +@Data +public class DevicePointRulesSaveReqVO { + + @Schema(description = "ID", requiredMode = Schema.RequiredMode.REQUIRED, example = "13120") + private Long id; + + @Schema(description = "标识符") + private String identifier; + + @Schema(description = "名称", example = "赵六") + private String fieldName; + + @Schema(description = "规则") + private String fieldRule; + + @Schema(description = "默认值") + private String defaultValue; + + @Schema(description = "设备模型Id", example = "19582") + private Long deviceId; + + @Schema(description = "点位规则列表", implementation = PointRulesRespVO.class) + private List pointRulesVOList; + + @Schema(description = "告警等级", example = "19582") + private String alarmLevel; +} \ No newline at end of file diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/devicewarinningrecord/DeviceWarinningRecordController.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/devicewarinningrecord/DeviceWarinningRecordController.java new file mode 100644 index 0000000..6dc94c2 --- /dev/null +++ b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/devicewarinningrecord/DeviceWarinningRecordController.java @@ -0,0 +1,116 @@ +package cn.iocoder.yudao.module.iot.controller.admin.devicewarinningrecord; + +import cn.iocoder.yudao.framework.apilog.core.annotation.ApiAccessLog; +import cn.iocoder.yudao.framework.common.pojo.CommonResult; +import cn.iocoder.yudao.framework.common.pojo.PageParam; +import cn.iocoder.yudao.framework.common.pojo.PageResult; +import cn.iocoder.yudao.framework.common.util.object.BeanUtils; +import cn.iocoder.yudao.framework.excel.core.util.ExcelUtils; +import cn.iocoder.yudao.module.iot.controller.admin.devicewarinningrecord.vo.DeviceWarinningRecordPageReqVO; +import cn.iocoder.yudao.module.iot.controller.admin.devicewarinningrecord.vo.DeviceWarinningRecordRespVO; +import cn.iocoder.yudao.module.iot.controller.admin.devicewarinningrecord.vo.DeviceWarinningRecordSaveReqVO; +import cn.iocoder.yudao.module.iot.dal.dataobject.devicewarinningrecord.DeviceWarinningRecordDO; +import cn.iocoder.yudao.module.iot.service.devicewarinningrecord.DeviceWarinningRecordService; +import io.swagger.v3.oas.annotations.Operation; +import io.swagger.v3.oas.annotations.Parameter; +import io.swagger.v3.oas.annotations.tags.Tag; +import org.springframework.security.access.prepost.PreAuthorize; +import org.springframework.validation.annotation.Validated; +import org.springframework.web.bind.annotation.*; + +import javax.annotation.Resource; +import javax.servlet.http.HttpServletResponse; +import javax.validation.Valid; +import java.io.IOException; +import java.util.List; +import java.util.Map; + +import static cn.iocoder.yudao.framework.apilog.core.enums.OperateTypeEnum.EXPORT; +import static cn.iocoder.yudao.framework.common.pojo.CommonResult.success; + +@Tag(name = "管理后台 - 告警记录") +@RestController +@RequestMapping("/iot/device-warinning-record") +@Validated +public class DeviceWarinningRecordController { + + @Resource + private DeviceWarinningRecordService deviceWarinningRecordService; + + @PostMapping("/create") + @Operation(summary = "创建告警记录") + @PreAuthorize("@ss.hasPermission('iot:device-warinning-record:create')") + public CommonResult createDeviceWarinningRecord(@Valid @RequestBody DeviceWarinningRecordSaveReqVO createReqVO) { + return success(deviceWarinningRecordService.createDeviceWarinningRecord(createReqVO)); + } + + @PutMapping("/update") + @Operation(summary = "更新告警记录") + @PreAuthorize("@ss.hasPermission('iot:device-warinning-record:update')") + public CommonResult updateDeviceWarinningRecord(@Valid @RequestBody DeviceWarinningRecordSaveReqVO updateReqVO) { + deviceWarinningRecordService.updateDeviceWarinningRecord(updateReqVO); + return success(true); + } + + @DeleteMapping("/delete") + @Operation(summary = "删除告警记录") + @Parameter(name = "id", description = "编号", required = true) + @PreAuthorize("@ss.hasPermission('iot:device-warinning-record:delete')") + public CommonResult deleteDeviceWarinningRecord(@RequestParam("id") Long id) { + deviceWarinningRecordService.deleteDeviceWarinningRecord(id); + return success(true); + } + + @GetMapping("/get") + @Operation(summary = "获得告警记录") + @Parameter(name = "id", description = "编号", required = true, example = "1024") + @PreAuthorize("@ss.hasPermission('iot:device-warinning-record:query')") + public CommonResult getDeviceWarinningRecord(@RequestParam("id") Long id) { + DeviceWarinningRecordDO deviceWarinningRecord = deviceWarinningRecordService.getDeviceWarinningRecord(id); + return success(BeanUtils.toBean(deviceWarinningRecord, DeviceWarinningRecordRespVO.class)); + } + + @GetMapping("/page") + @Operation(summary = "获得告警记录分页") + @PreAuthorize("@ss.hasPermission('iot:device-warinning-record:query')") + public CommonResult> getDeviceWarinningRecordPage(@Valid DeviceWarinningRecordPageReqVO pageReqVO) { + PageResult pageResult = deviceWarinningRecordService.getDeviceWarinningRecordPage(pageReqVO); + return success(BeanUtils.toBean(pageResult, DeviceWarinningRecordRespVO.class)); + } + + @GetMapping("/export-excel") + @Operation(summary = "导出告警记录 Excel") + @PreAuthorize("@ss.hasPermission('iot:device-warinning-record:export')") + @ApiAccessLog(operateType = EXPORT) + public void exportDeviceWarinningRecordExcel(@Valid DeviceWarinningRecordPageReqVO pageReqVO, + HttpServletResponse response) throws IOException { + pageReqVO.setPageSize(PageParam.PAGE_SIZE_NONE); + List list = deviceWarinningRecordService.getDeviceWarinningRecordPage(pageReqVO).getList(); + // 导出 Excel + ExcelUtils.write(response, "告警记录.xls", "数据", DeviceWarinningRecordRespVO.class, + BeanUtils.toBean(list, DeviceWarinningRecordRespVO.class)); + } + + + @GetMapping("/getList") + @Operation(summary = "获得告警记录列表") + @Parameter(name = "deviceId", description = "设备Id", required = true, example = "1024") + @Parameter(name = "orgId", description = "产线组织Id", required = true, example = "1024") + @PreAuthorize("@ss.hasPermission('iot:device-warinning-record:query')") + public CommonResult> getList(@RequestParam(name = "deviceId" ,required = false) Long id, + @RequestParam(name = "orgId" ,required = false) Long orgId) { + List deviceWarinningRecord = deviceWarinningRecordService.getList(id); + return success(deviceWarinningRecord); + } + + + @GetMapping("/getLastSevenHoursCount") + @Operation(summary = "获取过去7小时每小时告警数量") + @Parameter(name = "orgId", description = "产线组织Id", required = true, example = "1024") + @PreAuthorize("@ss.hasPermission('iot:device-warinning-record:query')") + public CommonResult>> getLastSevenHoursCount( @RequestParam(name = "orgId" ,required = false) Long orgId) { + List> hourCounts = deviceWarinningRecordService.getLastSevenHoursCount(); + return success(hourCounts); + } + +} \ No newline at end of file diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/devicewarinningrecord/vo/DeviceWarinningRecordPageReqVO.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/devicewarinningrecord/vo/DeviceWarinningRecordPageReqVO.java new file mode 100644 index 0000000..9f91761 --- /dev/null +++ b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/devicewarinningrecord/vo/DeviceWarinningRecordPageReqVO.java @@ -0,0 +1,42 @@ +package cn.iocoder.yudao.module.iot.controller.admin.devicewarinningrecord.vo; + +import cn.iocoder.yudao.framework.common.pojo.PageParam; +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.Data; +import lombok.EqualsAndHashCode; +import lombok.ToString; +import org.springframework.format.annotation.DateTimeFormat; + +import java.time.LocalDateTime; + +import static cn.iocoder.yudao.framework.common.util.date.DateUtils.FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND; + +@Schema(description = "管理后台 - 告警记录分页 Request VO") +@Data +@EqualsAndHashCode(callSuper = true) +@ToString(callSuper = true) +public class DeviceWarinningRecordPageReqVO extends PageParam { + + @Schema(description = "设备ID", example = "27939") + private Long deviceId; + + @Schema(description = "模型ID", example = "18634") + private Long modelId; + + @Schema(description = "规则值") + private String rule; + + @Schema(description = "告警等级") + private String alarmLevel; + + @Schema(description = "地址值") + private String addressValue; + + @Schema(description = "创建时间") + @DateTimeFormat(pattern = FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND) + private LocalDateTime[] createTime; + + @Schema(description = "点位规则Id", example = "25946") + private Long ruleId; + +} \ No newline at end of file diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/devicewarinningrecord/vo/DeviceWarinningRecordRespVO.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/devicewarinningrecord/vo/DeviceWarinningRecordRespVO.java new file mode 100644 index 0000000..0a9b694 --- /dev/null +++ b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/devicewarinningrecord/vo/DeviceWarinningRecordRespVO.java @@ -0,0 +1,57 @@ +package cn.iocoder.yudao.module.iot.controller.admin.devicewarinningrecord.vo; + +import com.alibaba.excel.annotation.ExcelIgnoreUnannotated; +import com.alibaba.excel.annotation.ExcelProperty; +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.Data; + +import java.time.LocalDateTime; + +@Schema(description = "管理后台 - 告警记录 Response VO") +@Data +@ExcelIgnoreUnannotated +public class DeviceWarinningRecordRespVO { + + @Schema(description = "ID", requiredMode = Schema.RequiredMode.REQUIRED, example = "22919") + @ExcelProperty("ID") + private Long id; + + @Schema(description = "设备ID", example = "27939") + @ExcelProperty("设备ID") + private Long deviceId; + + @Schema(description = "设备名称") + private String deviceName; + + @Schema(description = "点位模型ID", example = "18634") + @ExcelProperty("点位模型ID") + private Long modelId; + + @Schema(description = "点位模型名称") + private String modelName; + + @Schema(description = "规则值") + @ExcelProperty("规则值") + private String rule; + + @Schema(description = "告警等级") + @ExcelProperty("告警等级") + private String alarmLevel; + + @Schema(description = "地址值") + @ExcelProperty("地址值") + private String addressValue; + + @Schema(description = "创建时间", requiredMode = Schema.RequiredMode.REQUIRED) + @ExcelProperty("创建时间") + private LocalDateTime createTime; + + @Schema(description = "点位规则Id", example = "25946") + @ExcelProperty("点位规则Id") + private Long ruleId; + + @Schema(description = "点位规则名称", example = "25946") + private String ruleName; + + +} \ No newline at end of file diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/devicewarinningrecord/vo/DeviceWarinningRecordSaveReqVO.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/devicewarinningrecord/vo/DeviceWarinningRecordSaveReqVO.java new file mode 100644 index 0000000..3206de2 --- /dev/null +++ b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/devicewarinningrecord/vo/DeviceWarinningRecordSaveReqVO.java @@ -0,0 +1,31 @@ +package cn.iocoder.yudao.module.iot.controller.admin.devicewarinningrecord.vo; + +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.Data; + +@Schema(description = "管理后台 - 告警记录新增/修改 Request VO") +@Data +public class DeviceWarinningRecordSaveReqVO { + + @Schema(description = "ID", requiredMode = Schema.RequiredMode.REQUIRED, example = "22919") + private Long id; + + @Schema(description = "设备ID", example = "27939") + private Long deviceId; + + @Schema(description = "模型ID", example = "18634") + private Long modelId; + + @Schema(description = "规则值") + private String rule; + + @Schema(description = "告警等级") + private String alarmLevel; + + @Schema(description = "地址值") + private String addressValue; + + @Schema(description = "点位规则Id", example = "25946") + private Long ruleId; + +} \ No newline at end of file diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/gateway/GatewayController.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/gateway/GatewayController.java new file mode 100644 index 0000000..b9f9d50 --- /dev/null +++ b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/gateway/GatewayController.java @@ -0,0 +1,178 @@ +package cn.iocoder.yudao.module.iot.controller.admin.gateway; + +import cn.iocoder.yudao.framework.apilog.core.annotation.ApiAccessLog; +import cn.iocoder.yudao.framework.common.pojo.CommonResult; +import cn.iocoder.yudao.framework.common.pojo.PageParam; +import cn.iocoder.yudao.framework.common.pojo.PageResult; +import cn.iocoder.yudao.framework.common.util.object.BeanUtils; +import cn.iocoder.yudao.framework.excel.core.util.ExcelUtils; +import cn.iocoder.yudao.module.iot.controller.admin.gateway.vo.GatewayPageReqVO; +import cn.iocoder.yudao.module.iot.controller.admin.gateway.vo.GatewayRespVO; +import cn.iocoder.yudao.module.iot.controller.admin.gateway.vo.GatewaySaveReqVO; +import cn.iocoder.yudao.module.iot.dal.dataobject.device.DeviceDO; +import cn.iocoder.yudao.module.iot.dal.dataobject.gateway.GatewayDO; +import cn.iocoder.yudao.module.iot.framework.constant.IsEnableConstant; +import cn.iocoder.yudao.module.iot.framework.mqtt.consumer.IMqttservice; +import cn.iocoder.yudao.module.iot.service.gateway.GatewayService; +import io.swagger.v3.oas.annotations.Operation; +import io.swagger.v3.oas.annotations.Parameter; +import io.swagger.v3.oas.annotations.tags.Tag; +import org.apache.commons.lang3.StringUtils; +import org.eclipse.paho.client.mqttv3.MqttException; +import org.springframework.security.access.prepost.PreAuthorize; +import org.springframework.validation.annotation.Validated; +import org.springframework.web.bind.annotation.*; + +import javax.annotation.Resource; +import javax.servlet.http.HttpServletResponse; +import javax.validation.Valid; +import java.io.IOException; +import java.util.List; + +import static cn.iocoder.yudao.framework.apilog.core.enums.OperateTypeEnum.EXPORT; +import static cn.iocoder.yudao.framework.common.pojo.CommonResult.success; + +@Tag(name = "管理后台 - 网关") +@RestController +@RequestMapping("/iot/gateway") +@Validated +public class GatewayController { + + @Resource + private GatewayService gatewayService; + @Resource + private IMqttservice mqttservice; + + @PostMapping("/create") + @Operation(summary = "创建网关") + @PreAuthorize("@ss.hasPermission('iot:gateway:create')") + public CommonResult createGateway(@Valid @RequestBody GatewaySaveReqVO createReqVO) { + Long id = gatewayService.createGateway(createReqVO); + + if (id != null && IsEnableConstant.IsEnableTrue == createReqVO.getIsEnable() + && StringUtils.isNotBlank(createReqVO.getTopic())) { + try { + mqttservice.subscribeTopic(createReqVO.getTopic()); + } catch (MqttException e) { + e.printStackTrace(); + } + } + return success(id); + } + + @PutMapping("/update") + @Operation(summary = "更新网关") + @PreAuthorize("@ss.hasPermission('iot:gateway:update')") + public CommonResult updateGateway(@Valid @RequestBody GatewaySaveReqVO updateReqVO) { + GatewayDO old = gatewayService.getGateway(updateReqVO.getId()); + gatewayService.updateGateway(updateReqVO); + /**todo 订阅*/ + try { + if (old != null && StringUtils.isNotBlank(old.getTopic()) && StringUtils.isNotBlank(updateReqVO.getTopic())) { + boolean sameTopic = false; + if (old.getTopic().equals(updateReqVO.getTopic())) sameTopic = true; + if (sameTopic) { + //启用订阅 + if (updateReqVO.getIsEnable().equals(IsEnableConstant.IsEnableTrue)) + mqttservice.subscribeTopic(updateReqVO.getTopic()); + //禁用订阅 + else mqttservice.unsubscribeTopic(updateReqVO.getTopic()); + } else { + //禁用旧订阅 + if (old.getIsEnable().equals(IsEnableConstant.IsEnableTrue)) + mqttservice.unsubscribeTopic(old.getTopic()); + //启用新订阅 + if (updateReqVO.getIsEnable().equals(IsEnableConstant.IsEnableTrue)) + mqttservice.subscribeTopic(updateReqVO.getTopic()); + } + } + } catch (MqttException e) { + e.printStackTrace(); + } + + return success(true); + } + + @DeleteMapping("/delete") + @Operation(summary = "删除网关") + @Parameter(name = "id", description = "编号", required = true) + @PreAuthorize("@ss.hasPermission('iot:gateway:delete')") + public CommonResult deleteGateway(@RequestParam("id") Long id) { + gatewayService.deleteGateway(id); + return success(true); + } + + @GetMapping("/get") + @Operation(summary = "获得网关") + @Parameter(name = "id", description = "编号", required = true, example = "1024") + @PreAuthorize("@ss.hasPermission('iot:gateway:query')") + public CommonResult getGateway(@RequestParam("id") Long id) { + GatewayDO gateway = gatewayService.getGateway(id); + return success(BeanUtils.toBean(gateway, GatewayRespVO.class)); + } + + @GetMapping("/page") + @Operation(summary = "获得网关分页") + @PreAuthorize("@ss.hasPermission('iot:gateway:query')") + public CommonResult> getGatewayPage(@Valid GatewayPageReqVO pageReqVO) { + PageResult pageResult = gatewayService.getGatewayPage(pageReqVO); + return success(BeanUtils.toBean(pageResult, GatewayRespVO.class)); + } + + @GetMapping("/export-excel") + @Operation(summary = "导出网关 Excel") + @PreAuthorize("@ss.hasPermission('iot:gateway:export')") + @ApiAccessLog(operateType = EXPORT) + public void exportGatewayExcel(@Valid GatewayPageReqVO pageReqVO, + HttpServletResponse response) throws IOException { + pageReqVO.setPageSize(PageParam.PAGE_SIZE_NONE); + List list = gatewayService.getGatewayPage(pageReqVO).getList(); + // 导出 Excel + ExcelUtils.write(response, "网关.xls", "数据", GatewayRespVO.class, + BeanUtils.toBean(list, GatewayRespVO.class)); + } + + // ==================== 子表(物联设备) ==================== + + @GetMapping("/device/page") + @Operation(summary = "获得物联设备分页") + @Parameter(name = "gatewayId", description = "网关id") + @PreAuthorize("@ss.hasPermission('iot:gateway:query')") + public CommonResult> getDevicePage(PageParam pageReqVO, + @RequestParam("gatewayId") Long gatewayId) { + return success(gatewayService.getDevicePage(pageReqVO, gatewayId)); + } + + @PostMapping("/device/create") + @Operation(summary = "创建物联设备") + @PreAuthorize("@ss.hasPermission('iot:gateway:create')") + public CommonResult createDevice(@Valid @RequestBody DeviceDO device) { + return success(gatewayService.createDevice(device)); + } + + @PutMapping("/device/update") + @Operation(summary = "更新物联设备") + @PreAuthorize("@ss.hasPermission('iot:gateway:update')") + public CommonResult updateDevice(@Valid @RequestBody DeviceDO device) { + gatewayService.updateDevice(device); + return success(true); + } + + @DeleteMapping("/device/delete") + @Parameter(name = "id", description = "编号", required = true) + @Operation(summary = "删除物联设备") + @PreAuthorize("@ss.hasPermission('iot:gateway:delete')") + public CommonResult deleteDevice(@RequestParam("id") Long id) { + gatewayService.deleteDevice(id); + return success(true); + } + + @GetMapping("/device/get") + @Operation(summary = "获得物联设备") + @Parameter(name = "id", description = "编号", required = true) + @PreAuthorize("@ss.hasPermission('iot:gateway:query')") + public CommonResult getDevice(@RequestParam("id") Long id) { + return success(gatewayService.getDevice(id)); + } + +} \ No newline at end of file diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/gateway/vo/GatewayPageReqVO.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/gateway/vo/GatewayPageReqVO.java new file mode 100644 index 0000000..68886aa --- /dev/null +++ b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/gateway/vo/GatewayPageReqVO.java @@ -0,0 +1,51 @@ +package cn.iocoder.yudao.module.iot.controller.admin.gateway.vo; + +import cn.iocoder.yudao.framework.common.pojo.PageParam; +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.Data; +import lombok.EqualsAndHashCode; +import lombok.ToString; +import org.springframework.format.annotation.DateTimeFormat; + +import java.time.LocalDateTime; + +import static cn.iocoder.yudao.framework.common.util.date.DateUtils.FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND; + +@Schema(description = "管理后台 - 网关分页 Request VO") +@Data +@EqualsAndHashCode(callSuper = true) +@ToString(callSuper = true) +public class GatewayPageReqVO extends PageParam { + + @Schema(description = "管理地址") + private String adminIp; + + @Schema(description = "管理账号", example = "芋艿") + private String username; + + @Schema(description = "管理密码") + private String password; + + @Schema(description = "网关名字", example = "赵六") + private String gatewayName; + + @Schema(description = "备注", example = "随便") + private String remark; + + @Schema(description = "是否启用") + private Boolean isEnable; + + @Schema(description = "创建时间") + @DateTimeFormat(pattern = FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND) + private LocalDateTime[] createTime; + + @Schema(description = "网关编码") + private String gatewayCode; + + @Schema(description = "网关状态", example = "1") + private String gatewayStatus; + + @Schema(description = "订阅主题") + private String topic; + +} \ No newline at end of file diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/gateway/vo/GatewayRespVO.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/gateway/vo/GatewayRespVO.java new file mode 100644 index 0000000..9707131 --- /dev/null +++ b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/gateway/vo/GatewayRespVO.java @@ -0,0 +1,62 @@ +package cn.iocoder.yudao.module.iot.controller.admin.gateway.vo; + +import cn.iocoder.yudao.framework.excel.core.annotations.DictFormat; +import cn.iocoder.yudao.framework.excel.core.convert.DictConvert; +import com.alibaba.excel.annotation.ExcelIgnoreUnannotated; +import com.alibaba.excel.annotation.ExcelProperty; +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.Data; + +import java.time.LocalDateTime; + +@Schema(description = "管理后台 - 网关 Response VO") +@Data +@ExcelIgnoreUnannotated +public class GatewayRespVO { + + @Schema(description = "ID", requiredMode = Schema.RequiredMode.REQUIRED, example = "28060") + @ExcelProperty("ID") + private Long id; + + @Schema(description = "管理地址") + @ExcelProperty("管理地址") + private String adminIp; + + @Schema(description = "管理账号", example = "芋艿") + @ExcelProperty("管理账号") + private String username; + + @Schema(description = "管理密码") + @ExcelProperty("管理密码") + private String password; + + @Schema(description = "网关名字", example = "赵六") + @ExcelProperty("网关名字") + private String gatewayName; + + @Schema(description = "备注", example = "随便") + @ExcelProperty("备注") + private String remark; + + @Schema(description = "是否启用", requiredMode = Schema.RequiredMode.REQUIRED) + @ExcelProperty("是否启用") + private Boolean isEnable; + + @Schema(description = "创建时间", requiredMode = Schema.RequiredMode.REQUIRED) + @ExcelProperty("创建时间") + private LocalDateTime createTime; + + @Schema(description = "网关编码") + @ExcelProperty("网关编码") + private String gatewayCode; + + @Schema(description = "网关状态", example = "1") + @ExcelProperty(value = "网关状态", converter = DictConvert.class) + @DictFormat("iot_gateway_status") // TODO 代码优化:建议设置到对应的 DictTypeConstants 枚举类中 + private String gatewayStatus; + + @Schema(description = "订阅主题") + @ExcelProperty("订阅主题") + private String topic; + +} \ No newline at end of file diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/gateway/vo/GatewaySaveReqVO.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/gateway/vo/GatewaySaveReqVO.java new file mode 100644 index 0000000..a1b803a --- /dev/null +++ b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/gateway/vo/GatewaySaveReqVO.java @@ -0,0 +1,41 @@ +package cn.iocoder.yudao.module.iot.controller.admin.gateway.vo; + +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.Data; + +@Schema(description = "管理后台 - 网关新增/修改 Request VO") +@Data +public class GatewaySaveReqVO { + + @Schema(description = "ID", requiredMode = Schema.RequiredMode.REQUIRED, example = "28060") + private Long id; + + @Schema(description = "管理地址") + private String adminIp; + + @Schema(description = "管理账号", example = "芋艿") + private String username; + + @Schema(description = "管理密码") + private String password; + + @Schema(description = "网关名字", example = "赵六") + private String gatewayName; + + @Schema(description = "备注", example = "随便") + private String remark; + + @Schema(description = "是否启用", requiredMode = Schema.RequiredMode.REQUIRED) + + private Boolean isEnable; + + @Schema(description = "网关编码") + private String gatewayCode; + + @Schema(description = "网关状态", example = "1") + private String gatewayStatus; + + @Schema(description = "订阅主题") + private String topic; + +} \ No newline at end of file diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/orgnode/OrgNodeController.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/orgnode/OrgNodeController.java new file mode 100644 index 0000000..2fee65c --- /dev/null +++ b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/orgnode/OrgNodeController.java @@ -0,0 +1,110 @@ +package cn.iocoder.yudao.module.iot.controller.admin.orgnode; + +import org.springframework.web.bind.annotation.*; +import javax.annotation.Resource; +import org.springframework.validation.annotation.Validated; +import org.springframework.security.access.prepost.PreAuthorize; +import io.swagger.v3.oas.annotations.tags.Tag; +import io.swagger.v3.oas.annotations.Parameter; +import io.swagger.v3.oas.annotations.Operation; + +import javax.validation.constraints.*; +import javax.validation.*; +import javax.servlet.http.*; +import java.util.*; +import java.io.IOException; + +import cn.iocoder.yudao.framework.common.pojo.PageParam; +import cn.iocoder.yudao.framework.common.pojo.PageResult; +import cn.iocoder.yudao.framework.common.pojo.CommonResult; +import cn.iocoder.yudao.framework.common.util.object.BeanUtils; +import static cn.iocoder.yudao.framework.common.pojo.CommonResult.success; + +import cn.iocoder.yudao.framework.excel.core.util.ExcelUtils; + +import cn.iocoder.yudao.framework.apilog.core.annotation.ApiAccessLog; +import static cn.iocoder.yudao.framework.apilog.core.enums.OperateTypeEnum.*; + +import cn.iocoder.yudao.module.iot.controller.admin.orgnode.vo.*; +import cn.iocoder.yudao.module.iot.dal.dataobject.orgnode.OrgNodeDO; +import cn.iocoder.yudao.module.iot.service.orgnode.OrgNodeService; + +@Tag(name = "管理后台 - 客户组织节点表,用于管理客户、车间、产线等多级组织结构") +@RestController +@RequestMapping("/iot/org-node") +@Validated +public class OrgNodeController { + + @Resource + private OrgNodeService orgNodeService; + + @PostMapping("/create") + @Operation(summary = "创建客户组织节点表,用于管理客户、车间、产线等多级组织结构") + @PreAuthorize("@ss.hasPermission('iot:org-node:create')") + public CommonResult createOrgNode(@Valid @RequestBody OrgNodeSaveReqVO createReqVO) { + return success(orgNodeService.createOrgNode(createReqVO)); + } + + @PutMapping("/update") + @Operation(summary = "更新客户组织节点表,用于管理客户、车间、产线等多级组织结构") + @PreAuthorize("@ss.hasPermission('iot:org-node:update')") + public CommonResult updateOrgNode(@Valid @RequestBody OrgNodeSaveReqVO updateReqVO) { + orgNodeService.updateOrgNode(updateReqVO); + return success(true); + } + + @DeleteMapping("/delete") + @Operation(summary = "删除客户组织节点表,用于管理客户、车间、产线等多级组织结构") + @Parameter(name = "id", description = "编号", required = true) + @PreAuthorize("@ss.hasPermission('iot:org-node:delete')") + public CommonResult deleteOrgNode(@RequestParam("id") Long id) { + orgNodeService.deleteOrgNode(id); + return success(true); + } + + @DeleteMapping("/delete-list") + @Parameter(name = "ids", description = "编号", required = true) + @Operation(summary = "批量删除客户组织节点表,用于管理客户、车间、产线等多级组织结构") + @PreAuthorize("@ss.hasPermission('iot:org-node:delete')") + public CommonResult deleteOrgNodeList(@RequestParam("ids") List ids) { + orgNodeService.deleteOrgNodeListByIds(ids); + return success(true); + } + + @GetMapping("/get") + @Operation(summary = "获得客户组织节点表,用于管理客户、车间、产线等多级组织结构") + @Parameter(name = "id", description = "编号", required = true, example = "1024") + @PreAuthorize("@ss.hasPermission('iot:org-node:query')") + public CommonResult getOrgNode(@RequestParam("id") Long id) { + OrgNodeDO orgNode = orgNodeService.getOrgNode(id); + return success(BeanUtils.toBean(orgNode, OrgNodeRespVO.class)); + } + + @GetMapping("/page") + @Operation(summary = "获得客户组织节点表,用于管理客户、车间、产线等多级组织结构分页") + @PreAuthorize("@ss.hasPermission('iot:org-node:query')") + public CommonResult> getOrgNodePage(@Valid OrgNodePageReqVO pageReqVO) { + PageResult pageResult = orgNodeService.getOrgNodePage(pageReqVO); + return success(BeanUtils.toBean(pageResult, OrgNodeRespVO.class)); + } + + @GetMapping("/export-excel") + @Operation(summary = "导出客户组织节点表,用于管理客户、车间、产线等多级组织结构 Excel") + @PreAuthorize("@ss.hasPermission('iot:org-node:export')") + @ApiAccessLog(operateType = EXPORT) + public void exportOrgNodeExcel(@Valid OrgNodePageReqVO pageReqVO, + HttpServletResponse response) throws IOException { + pageReqVO.setPageSize(PageParam.PAGE_SIZE_NONE); + List list = orgNodeService.getOrgNodePage(pageReqVO).getList(); + // 导出 Excel + ExcelUtils.write(response, "客户组织节点表,用于管理客户、车间、产线等多级组织结构.xls", "数据", OrgNodeRespVO.class, + BeanUtils.toBean(list, OrgNodeRespVO.class)); + } + + @GetMapping("/tree") + @Operation(summary = "查询客户组织树") + public CommonResult> getOrgTree(@RequestParam(required = false) Long customerId) { + return CommonResult.success(orgNodeService.getOrgTree(customerId)); + } + +} \ No newline at end of file diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/orgnode/vo/OrgNodePageReqVO.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/orgnode/vo/OrgNodePageReqVO.java new file mode 100644 index 0000000..1f15c21 --- /dev/null +++ b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/orgnode/vo/OrgNodePageReqVO.java @@ -0,0 +1,35 @@ +package cn.iocoder.yudao.module.iot.controller.admin.orgnode.vo; + +import lombok.*; +import java.util.*; +import io.swagger.v3.oas.annotations.media.Schema; +import cn.iocoder.yudao.framework.common.pojo.PageParam; +import org.springframework.format.annotation.DateTimeFormat; +import java.time.LocalDateTime; + +import static cn.iocoder.yudao.framework.common.util.date.DateUtils.FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND; + +@Schema(description = "管理后台 - 客户组织节点表,用于管理客户、车间、产线等多级组织结构分页 Request VO") +@Data +public class OrgNodePageReqVO extends PageParam { + + @Schema(description = "客户ID", example = "17297") + private Long customerId; + + @Schema(description = "父节点ID,0表示根节点", example = "26900") + private Long parentId; + + @Schema(description = "节点类型:1客户 2车间 3产线", example = "1") + private Integer nodeType; + + @Schema(description = "节点名称", example = "赵六") + private String name; + + @Schema(description = "排序字段,数值越小越靠前") + private Integer sort; + + @Schema(description = "创建时间") + @DateTimeFormat(pattern = FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND) + private LocalDateTime[] createTime; + +} \ No newline at end of file diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/orgnode/vo/OrgNodeRespVO.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/orgnode/vo/OrgNodeRespVO.java new file mode 100644 index 0000000..8ffb490 --- /dev/null +++ b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/orgnode/vo/OrgNodeRespVO.java @@ -0,0 +1,44 @@ +package cn.iocoder.yudao.module.iot.controller.admin.orgnode.vo; + +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.*; +import java.util.*; +import org.springframework.format.annotation.DateTimeFormat; +import java.time.LocalDateTime; +import com.alibaba.excel.annotation.ExcelIgnoreUnannotated; +import com.alibaba.excel.annotation.ExcelProperty; + +@Schema(description = "管理后台 - 客户组织节点表,用于管理客户、车间、产线等多级组织结构 Response VO") +@Data +@ExcelIgnoreUnannotated +public class OrgNodeRespVO { + + @Schema(description = "主键ID,自增长", requiredMode = Schema.RequiredMode.REQUIRED, example = "16501") + @ExcelProperty("主键ID,自增长") + private Long id; + + @Schema(description = "客户ID", requiredMode = Schema.RequiredMode.REQUIRED, example = "17297") + @ExcelProperty("客户ID") + private Long customerId; + + @Schema(description = "父节点ID,0表示根节点", requiredMode = Schema.RequiredMode.REQUIRED, example = "26900") + @ExcelProperty("父节点ID,0表示根节点") + private Long parentId; + + @Schema(description = "节点类型:1客户 2车间 3产线", requiredMode = Schema.RequiredMode.REQUIRED, example = "1") + @ExcelProperty("节点类型:1客户 2车间 3产线") + private Integer nodeType; + + @Schema(description = "节点名称", requiredMode = Schema.RequiredMode.REQUIRED, example = "赵六") + @ExcelProperty("节点名称") + private String name; + + @Schema(description = "排序字段,数值越小越靠前", requiredMode = Schema.RequiredMode.REQUIRED) + @ExcelProperty("排序字段,数值越小越靠前") + private Integer sort; + + @Schema(description = "创建时间", requiredMode = Schema.RequiredMode.REQUIRED) + @ExcelProperty("创建时间") + private LocalDateTime createTime; + +} \ No newline at end of file diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/orgnode/vo/OrgNodeSaveReqVO.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/orgnode/vo/OrgNodeSaveReqVO.java new file mode 100644 index 0000000..5119b72 --- /dev/null +++ b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/orgnode/vo/OrgNodeSaveReqVO.java @@ -0,0 +1,35 @@ +package cn.iocoder.yudao.module.iot.controller.admin.orgnode.vo; + +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.*; +import java.util.*; +import javax.validation.constraints.*; + +@Schema(description = "管理后台 - 客户组织节点表,用于管理客户、车间、产线等多级组织结构新增/修改 Request VO") +@Data +public class OrgNodeSaveReqVO { + + @Schema(description = "主键ID,自增长", requiredMode = Schema.RequiredMode.REQUIRED, example = "16501") + private Long id; + + @Schema(description = "客户ID", requiredMode = Schema.RequiredMode.REQUIRED, example = "17297") + @NotNull(message = "客户ID不能为空") + private Long customerId; + + @Schema(description = "父节点ID,0表示根节点", requiredMode = Schema.RequiredMode.REQUIRED, example = "26900") + @NotNull(message = "父节点ID,0表示根节点不能为空") + private Long parentId; + + @Schema(description = "节点类型:1客户 2车间 3产线", requiredMode = Schema.RequiredMode.REQUIRED, example = "1") + @NotNull(message = "节点类型:1客户 2车间 3产线不能为空") + private Integer nodeType; + + @Schema(description = "节点名称", requiredMode = Schema.RequiredMode.REQUIRED, example = "赵六") + @NotEmpty(message = "节点名称不能为空") + private String name; + + @Schema(description = "排序字段,数值越小越靠前", requiredMode = Schema.RequiredMode.REQUIRED) + @NotNull(message = "排序字段,数值越小越靠前不能为空") + private Integer sort; + +} \ No newline at end of file diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/orgnode/vo/OrgNodeTreeRespVO.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/orgnode/vo/OrgNodeTreeRespVO.java new file mode 100644 index 0000000..eb727d1 --- /dev/null +++ b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/orgnode/vo/OrgNodeTreeRespVO.java @@ -0,0 +1,14 @@ +package cn.iocoder.yudao.module.iot.controller.admin.orgnode.vo; + +import lombok.Data; + +import java.util.List; + +@Data +public class OrgNodeTreeRespVO { + private Long id; + private Long parentId; + private Integer nodeType; // 1客户 2车间 3产线 + private String name; + private List children; +} \ No newline at end of file diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/ota/IotOtaFirmwareController.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/ota/IotOtaFirmwareController.java deleted file mode 100644 index e864110..0000000 --- a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/ota/IotOtaFirmwareController.java +++ /dev/null @@ -1,75 +0,0 @@ -package cn.iocoder.yudao.module.iot.controller.admin.ota; - -import cn.iocoder.yudao.framework.common.pojo.CommonResult; -import cn.iocoder.yudao.framework.common.pojo.PageResult; -import cn.iocoder.yudao.framework.common.util.object.BeanUtils; -import cn.iocoder.yudao.module.iot.controller.admin.ota.vo.firmware.IotOtaFirmwareCreateReqVO; -import cn.iocoder.yudao.module.iot.controller.admin.ota.vo.firmware.IotOtaFirmwarePageReqVO; -import cn.iocoder.yudao.module.iot.controller.admin.ota.vo.firmware.IotOtaFirmwareRespVO; -import cn.iocoder.yudao.module.iot.controller.admin.ota.vo.firmware.IotOtaFirmwareUpdateReqVO; -import cn.iocoder.yudao.module.iot.dal.dataobject.ota.IotOtaFirmwareDO; -import cn.iocoder.yudao.module.iot.dal.dataobject.product.IotProductDO; -import cn.iocoder.yudao.module.iot.service.ota.IotOtaFirmwareService; -import cn.iocoder.yudao.module.iot.service.product.IotProductService; -import io.swagger.v3.oas.annotations.Operation; -import io.swagger.v3.oas.annotations.tags.Tag; -import org.springframework.security.access.prepost.PreAuthorize; -import org.springframework.validation.annotation.Validated; -import org.springframework.web.bind.annotation.*; - -import javax.annotation.Resource; -import javax.validation.Valid; - -import static cn.iocoder.yudao.framework.common.pojo.CommonResult.success; - -@Tag(name = "管理后台 - IoT OTA 固件") -@RestController -@RequestMapping("/iot/ota/firmware") -@Validated -public class IotOtaFirmwareController { - - @Resource - private IotOtaFirmwareService otaFirmwareService; - @Resource - private IotProductService productService; - - @PostMapping("/create") - @Operation(summary = "创建 OTA 固件") - @PreAuthorize("@ss.hasPermission('iot:ota-firmware:create')") - public CommonResult createOtaFirmware(@Valid @RequestBody IotOtaFirmwareCreateReqVO createReqVO) { - return success(otaFirmwareService.createOtaFirmware(createReqVO)); - } - - @PutMapping("/update") - @Operation(summary = "更新 OTA 固件") - @PreAuthorize("@ss.hasPermission('iot:ota-firmware:update')") - public CommonResult updateOtaFirmware(@Valid @RequestBody IotOtaFirmwareUpdateReqVO updateReqVO) { - otaFirmwareService.updateOtaFirmware(updateReqVO); - return success(true); - } - - @GetMapping("/get") - @Operation(summary = "获得 OTA 固件") - @PreAuthorize("@ss.hasPermission('iot:ota-firmware:query')") - public CommonResult getOtaFirmware(@RequestParam("id") Long id) { - IotOtaFirmwareDO firmware = otaFirmwareService.getOtaFirmware(id); - if (firmware == null) { - return success(null); - } - return success(BeanUtils.toBean(firmware, IotOtaFirmwareRespVO.class, o -> { - IotProductDO product = productService.getProduct(firmware.getProductId()); - if (product != null) { - o.setProductName(product.getName()); - } - })); - } - - @GetMapping("/page") - @Operation(summary = "获得 OTA 固件分页") - @PreAuthorize("@ss.hasPermission('iot:ota-firmware:query')") - public CommonResult> getOtaFirmwarePage(@Valid IotOtaFirmwarePageReqVO pageReqVO) { - PageResult pageResult = otaFirmwareService.getOtaFirmwarePage(pageReqVO); - return success(BeanUtils.toBean(pageResult, IotOtaFirmwareRespVO.class)); - } - -} diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/ota/IotOtaTaskController.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/ota/IotOtaTaskController.java deleted file mode 100644 index 6e5b4d1..0000000 --- a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/ota/IotOtaTaskController.java +++ /dev/null @@ -1,65 +0,0 @@ -package cn.iocoder.yudao.module.iot.controller.admin.ota; - -import cn.iocoder.yudao.framework.common.pojo.CommonResult; -import cn.iocoder.yudao.framework.common.pojo.PageResult; -import cn.iocoder.yudao.framework.common.util.object.BeanUtils; -import cn.iocoder.yudao.module.iot.controller.admin.ota.vo.task.IotOtaTaskCreateReqVO; -import cn.iocoder.yudao.module.iot.controller.admin.ota.vo.task.IotOtaTaskPageReqVO; -import cn.iocoder.yudao.module.iot.controller.admin.ota.vo.task.IotOtaTaskRespVO; -import cn.iocoder.yudao.module.iot.dal.dataobject.ota.IotOtaTaskDO; -import cn.iocoder.yudao.module.iot.service.ota.IotOtaTaskService; -import io.swagger.v3.oas.annotations.Operation; -import io.swagger.v3.oas.annotations.Parameter; -import io.swagger.v3.oas.annotations.tags.Tag; -import org.springframework.security.access.prepost.PreAuthorize; -import org.springframework.validation.annotation.Validated; -import org.springframework.web.bind.annotation.*; - -import javax.annotation.Resource; -import javax.validation.Valid; - -import static cn.iocoder.yudao.framework.common.pojo.CommonResult.success; - -@Tag(name = "管理后台 - IoT OTA 升级任务") -@RestController -@RequestMapping("/iot/ota/task") -@Validated -public class IotOtaTaskController { - - @Resource - private IotOtaTaskService otaTaskService; - - @PostMapping("/create") - @Operation(summary = "创建 OTA 升级任务") - @PreAuthorize(value = "@ss.hasPermission('iot:ota-task:create')") - public CommonResult createOtaTask(@Valid @RequestBody IotOtaTaskCreateReqVO createReqVO) { - return success(otaTaskService.createOtaTask(createReqVO)); - } - - @PostMapping("/cancel") - @Operation(summary = "取消 OTA 升级任务") - @Parameter(name = "id", description = "升级任务编号", required = true) - @PreAuthorize(value = "@ss.hasPermission('iot:ota-task:cancel')") - public CommonResult cancelOtaTask(@RequestParam("id") Long id) { - otaTaskService.cancelOtaTask(id); - return success(true); - } - - @GetMapping("/page") - @Operation(summary = "获得 OTA 升级任务分页") - @PreAuthorize(value = "@ss.hasPermission('iot:ota-task:query')") - public CommonResult> getOtaTaskPage(@Valid IotOtaTaskPageReqVO pageReqVO) { - PageResult pageResult = otaTaskService.getOtaTaskPage(pageReqVO); - return success(BeanUtils.toBean(pageResult, IotOtaTaskRespVO.class)); - } - - @GetMapping("/get") - @Operation(summary = "获得 OTA 升级任务") - @Parameter(name = "id", description = "升级任务编号", required = true, example = "1024") - @PreAuthorize(value = "@ss.hasPermission('iot:ota-task:query')") - public CommonResult getOtaTask(@RequestParam("id") Long id) { - IotOtaTaskDO upgradeTask = otaTaskService.getOtaTask(id); - return success(BeanUtils.toBean(upgradeTask, IotOtaTaskRespVO.class)); - } - -} diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/ota/IotOtaTaskRecordController.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/ota/IotOtaTaskRecordController.java deleted file mode 100644 index da4dc61..0000000 --- a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/ota/IotOtaTaskRecordController.java +++ /dev/null @@ -1,99 +0,0 @@ -package cn.iocoder.yudao.module.iot.controller.admin.ota; - -import cn.hutool.core.collection.CollUtil; -import cn.iocoder.yudao.framework.common.pojo.CommonResult; -import cn.iocoder.yudao.framework.common.pojo.PageResult; -import cn.iocoder.yudao.framework.common.util.collection.MapUtils; -import cn.iocoder.yudao.framework.common.util.object.BeanUtils; -import cn.iocoder.yudao.module.iot.controller.admin.ota.vo.task.record.IotOtaTaskRecordPageReqVO; -import cn.iocoder.yudao.module.iot.controller.admin.ota.vo.task.record.IotOtaTaskRecordRespVO; -import cn.iocoder.yudao.module.iot.dal.dataobject.device.IotDeviceDO; -import cn.iocoder.yudao.module.iot.dal.dataobject.ota.IotOtaFirmwareDO; -import cn.iocoder.yudao.module.iot.dal.dataobject.ota.IotOtaTaskRecordDO; -import cn.iocoder.yudao.module.iot.service.device.IotDeviceService; -import cn.iocoder.yudao.module.iot.service.ota.IotOtaFirmwareService; -import cn.iocoder.yudao.module.iot.service.ota.IotOtaTaskRecordService; -import io.swagger.v3.oas.annotations.Operation; -import io.swagger.v3.oas.annotations.Parameter; -import io.swagger.v3.oas.annotations.Parameters; -import io.swagger.v3.oas.annotations.tags.Tag; -import org.springframework.security.access.prepost.PreAuthorize; -import org.springframework.validation.annotation.Validated; -import org.springframework.web.bind.annotation.*; - -import javax.annotation.Resource; -import javax.validation.Valid; -import java.util.Map; - -import static cn.iocoder.yudao.framework.common.pojo.CommonResult.success; -import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertSet; - -@Tag(name = "管理后台 - IoT OTA 升级任务记录") -@RestController -@RequestMapping("/iot/ota/task/record") -@Validated -public class IotOtaTaskRecordController { - - @Resource - private IotOtaTaskRecordService otaTaskRecordService; - @Resource - private IotDeviceService deviceService; - @Resource - private IotOtaFirmwareService otaFirmwareService; - - @GetMapping("/get-status-statistics") - @Operation(summary = "获得 OTA 升级记录状态统计") - @Parameters({ - @Parameter(name = "firmwareId", description = "固件编号", example = "1024"), - @Parameter(name = "taskId", description = "升级任务编号", example = "2048") - }) - @PreAuthorize("@ss.hasPermission('iot:ota-task-record:query')") - public CommonResult> getOtaTaskRecordStatusStatistics( - @RequestParam(value = "firmwareId", required = false) Long firmwareId, - @RequestParam(value = "taskId", required = false) Long taskId) { - return success(otaTaskRecordService.getOtaTaskRecordStatusStatistics(firmwareId, taskId)); - } - - @GetMapping("/page") - @Operation(summary = "获得 OTA 升级记录分页") - @PreAuthorize("@ss.hasPermission('iot:ota-task-record:query')") - public CommonResult> getOtaTaskRecordPage( - @Valid IotOtaTaskRecordPageReqVO pageReqVO) { - PageResult pageResult = otaTaskRecordService.getOtaTaskRecordPage(pageReqVO); - if (CollUtil.isEmpty(pageResult.getList())) { - return success(PageResult.empty()); - } - - // 批量查询固件信息 - Map firmwareMap = otaFirmwareService.getOtaFirmwareMap( - convertSet(pageResult.getList(), IotOtaTaskRecordDO::getFromFirmwareId)); - Map deviceMap = deviceService.getDeviceMap( - convertSet(pageResult.getList(), IotOtaTaskRecordDO::getDeviceId)); - // 转换为响应 VO - return success(BeanUtils.toBean(pageResult, IotOtaTaskRecordRespVO.class, (vo) -> { - MapUtils.findAndThen(firmwareMap, vo.getFromFirmwareId(), firmware -> - vo.setFromFirmwareVersion(firmware.getVersion())); - MapUtils.findAndThen(deviceMap, vo.getDeviceId(), device -> - vo.setDeviceName(device.getDeviceName())); - })); - } - - @GetMapping("/get") - @Operation(summary = "获得 OTA 升级记录") - @PreAuthorize("@ss.hasPermission('iot:ota-task-record:query')") - @Parameter(name = "id", description = "升级记录编号", required = true, example = "1024") - public CommonResult getOtaTaskRecord(@RequestParam("id") Long id) { - IotOtaTaskRecordDO upgradeRecord = otaTaskRecordService.getOtaTaskRecord(id); - return success(BeanUtils.toBean(upgradeRecord, IotOtaTaskRecordRespVO.class)); - } - - @PutMapping("/cancel") - @Operation(summary = "取消 OTA 升级记录") - @PreAuthorize("@ss.hasPermission('iot:ota-task-record:cancel')") - @Parameter(name = "id", description = "升级记录编号", required = true, example = "1024") - public CommonResult cancelOtaTaskRecord(@RequestParam("id") Long id) { - otaTaskRecordService.cancelOtaTaskRecord(id); - return success(true); - } - -} diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/ota/vo/firmware/IotOtaFirmwareCreateReqVO.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/ota/vo/firmware/IotOtaFirmwareCreateReqVO.java deleted file mode 100644 index 355a08d..0000000 --- a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/ota/vo/firmware/IotOtaFirmwareCreateReqVO.java +++ /dev/null @@ -1,34 +0,0 @@ -package cn.iocoder.yudao.module.iot.controller.admin.ota.vo.firmware; - -import io.swagger.v3.oas.annotations.media.Schema; -import lombok.Data; -import org.hibernate.validator.constraints.URL; - -import javax.validation.constraints.NotEmpty; -import javax.validation.constraints.NotNull; - -@Schema(description = "管理后台 - IoT OTA 固件创建 Request VO") -@Data -public class IotOtaFirmwareCreateReqVO { - - @Schema(description = "固件名称", requiredMode = Schema.RequiredMode.REQUIRED, example = "智能开关固件") - @NotEmpty(message = "固件名称不能为空") - private String name; - - @Schema(description = "固件描述", example = "某品牌型号固件,测试用") - private String description; - - @Schema(description = "版本号", requiredMode = Schema.RequiredMode.REQUIRED, example = "1.0.0") - @NotEmpty(message = "版本号不能为空") - private String version; - - @Schema(description = "产品编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "1024") - @NotNull(message = "产品编号不能为空") - private Long productId; - - @Schema(description = "固件文件 URL", requiredMode = Schema.RequiredMode.REQUIRED, example = "https://www.iocoder.cn/1.zip") - @NotEmpty(message = "固件文件 URL 不能为空") - @URL(message = "固件文件 URL 格式错误") - private String fileUrl; - -} diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/ota/vo/firmware/IotOtaFirmwarePageReqVO.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/ota/vo/firmware/IotOtaFirmwarePageReqVO.java deleted file mode 100644 index 589ed00..0000000 --- a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/ota/vo/firmware/IotOtaFirmwarePageReqVO.java +++ /dev/null @@ -1,26 +0,0 @@ -package cn.iocoder.yudao.module.iot.controller.admin.ota.vo.firmware; - -import cn.iocoder.yudao.framework.common.pojo.PageParam; -import io.swagger.v3.oas.annotations.media.Schema; -import lombok.Data; -import org.springframework.format.annotation.DateTimeFormat; - -import java.time.LocalDateTime; - -import static cn.iocoder.yudao.framework.common.util.date.DateUtils.FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND; - -@Schema(description = "管理后台 - IoT OTA 固件分页 Request VO") -@Data -public class IotOtaFirmwarePageReqVO extends PageParam { - - @Schema(description = "固件名称", example = "智能开关固件") - private String name; - - @Schema(description = "产品标识", example = "1024") - private String productId; - - @Schema(description = "创建时间", example = "[2022-07-01 00:00:00, 2022-07-01 23:59:59]") - @DateTimeFormat(pattern = FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND) - private LocalDateTime[] createTime; - -} diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/ota/vo/firmware/IotOtaFirmwareRespVO.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/ota/vo/firmware/IotOtaFirmwareRespVO.java deleted file mode 100644 index d6fdbf7..0000000 --- a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/ota/vo/firmware/IotOtaFirmwareRespVO.java +++ /dev/null @@ -1,49 +0,0 @@ -package cn.iocoder.yudao.module.iot.controller.admin.ota.vo.firmware; - -import com.fhs.core.trans.vo.VO; -import io.swagger.v3.oas.annotations.media.Schema; -import lombok.Data; - -import java.time.LocalDateTime; - -@Schema(description = "管理后台 - IoT OTA 固件 Response VO") -@Data -public class IotOtaFirmwareRespVO implements VO { - - @Schema(description = "固件编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "1024") - private Long id; - - @Schema(description = "固件名称", requiredMode = Schema.RequiredMode.REQUIRED, example = "OTA 固件") - private String name; - - @Schema(description = "固件描述") - private String description; - - @Schema(description = "版本号", requiredMode = Schema.RequiredMode.REQUIRED, example = "1.0.0") - private String version; - - @Schema(description = "产品编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "1024") - private Long productId; - - @Schema(description = "产品名称", requiredMode = Schema.RequiredMode.REQUIRED, example = "智能设备") - private String productName; - - @Schema(description = "固件文件 URL", requiredMode = Schema.RequiredMode.REQUIRED, example = "https://www.iocoder.cn/firmware.bin") - private String fileUrl; - - @Schema(description = "固件文件大小", requiredMode = Schema.RequiredMode.REQUIRED, example = "1024") - private Long fileSize; - - @Schema(description = "固件文件签名算法", example = "MD5") - private String fileDigestAlgorithm; - - @Schema(description = "固件文件签名结果", example = "d41d8cd98f00b204e9800998ecf8427e") - private String fileDigestValue; - - @Schema(description = "创建时间", requiredMode = Schema.RequiredMode.REQUIRED) - private LocalDateTime createTime; - - @Schema(description = "更新时间", requiredMode = Schema.RequiredMode.REQUIRED) - private LocalDateTime updateTime; - -} diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/ota/vo/firmware/IotOtaFirmwareUpdateReqVO.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/ota/vo/firmware/IotOtaFirmwareUpdateReqVO.java deleted file mode 100644 index 06a579b..0000000 --- a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/ota/vo/firmware/IotOtaFirmwareUpdateReqVO.java +++ /dev/null @@ -1,22 +0,0 @@ -package cn.iocoder.yudao.module.iot.controller.admin.ota.vo.firmware; - -import io.swagger.v3.oas.annotations.media.Schema; -import lombok.Data; - -import javax.validation.constraints.NotNull; - -@Schema(description = "管理后台 - IoT OTA 固件更新 Request VO") -@Data -public class IotOtaFirmwareUpdateReqVO { - - @Schema(description = "固件编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "1024") - @NotNull(message = "固件编号不能为空") - private Long id; - - @Schema(description = "固件名称", example = "智能开关固件") - private String name; - - @Schema(description = "固件描述", example = "某品牌型号固件,测试用") - private String description; - -} diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/ota/vo/task/IotOtaTaskCreateReqVO.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/ota/vo/task/IotOtaTaskCreateReqVO.java deleted file mode 100644 index 33e3016..0000000 --- a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/ota/vo/task/IotOtaTaskCreateReqVO.java +++ /dev/null @@ -1,37 +0,0 @@ -package cn.iocoder.yudao.module.iot.controller.admin.ota.vo.task; - -import cn.iocoder.yudao.framework.common.validation.InEnum; -import cn.iocoder.yudao.module.iot.enums.ota.IotOtaTaskDeviceScopeEnum; -import io.swagger.v3.oas.annotations.media.Schema; -import lombok.Data; - -import javax.validation.constraints.NotEmpty; -import javax.validation.constraints.NotNull; -import java.util.List; - -@Schema(description = "管理后台 - IoT OTA 升级任务创建 Request VO") -@Data -public class IotOtaTaskCreateReqVO { - - @NotEmpty(message = "任务名称不能为空") - @Schema(description = "任务名称", requiredMode = Schema.RequiredMode.REQUIRED, example = "升级任务") - private String name; - - @Schema(description = "任务描述", example = "升级任务") - private String description; - - @Schema(description = "固件编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "1024") - @NotNull(message = "固件编号不能为空") - private Long firmwareId; - - @Schema(description = "升级范围", requiredMode = Schema.RequiredMode.REQUIRED, example = "1") - @NotNull(message = "升级范围不能为空") - @InEnum(value = IotOtaTaskDeviceScopeEnum.class) - private Integer deviceScope; - - @Schema(description = "选中的设备编号数组", requiredMode = Schema.RequiredMode.REQUIRED, example = "1,2,3") - private List deviceIds; - - // TODO @li:如果 deviceScope 等于 2 时,deviceIds 校验非空; - -} diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/ota/vo/task/IotOtaTaskPageReqVO.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/ota/vo/task/IotOtaTaskPageReqVO.java deleted file mode 100644 index 4638f1a..0000000 --- a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/ota/vo/task/IotOtaTaskPageReqVO.java +++ /dev/null @@ -1,17 +0,0 @@ -package cn.iocoder.yudao.module.iot.controller.admin.ota.vo.task; - -import cn.iocoder.yudao.framework.common.pojo.PageParam; -import io.swagger.v3.oas.annotations.media.Schema; -import lombok.Data; - -@Schema(description = "管理后台 - IoT OTA 升级任务分页 Request VO") -@Data -public class IotOtaTaskPageReqVO extends PageParam { - - @Schema(description = "任务名称", example = "升级任务") - private String name; - - @Schema(description = "固件编号", example = "1024") - private Long firmwareId; - -} diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/ota/vo/task/IotOtaTaskRespVO.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/ota/vo/task/IotOtaTaskRespVO.java deleted file mode 100644 index 247f7c6..0000000 --- a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/ota/vo/task/IotOtaTaskRespVO.java +++ /dev/null @@ -1,40 +0,0 @@ -package cn.iocoder.yudao.module.iot.controller.admin.ota.vo.task; - -import com.fhs.core.trans.vo.VO; -import io.swagger.v3.oas.annotations.media.Schema; -import lombok.Data; - -import java.time.LocalDateTime; - -@Schema(description = "管理后台 - IoT OTA 升级任务 Response VO") -@Data -public class IotOtaTaskRespVO implements VO { - - @Schema(description = "任务编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "1024") - private Long id; - - @Schema(description = "任务名称", requiredMode = Schema.RequiredMode.REQUIRED, example = "升级任务") - private String name; - - @Schema(description = "任务描述", example = "升级任务") - private String description; - - @Schema(description = "固件编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "1024") - private Long firmwareId; - - @Schema(description = "任务状态", requiredMode = Schema.RequiredMode.REQUIRED, example = "10") - private Integer status; - - @Schema(description = "升级范围", requiredMode = Schema.RequiredMode.REQUIRED, example = "1") - private Integer deviceScope; - - @Schema(description = "设备总共数量", requiredMode = Schema.RequiredMode.REQUIRED, example = "1024") - private Integer deviceTotalCount; - - @Schema(description = "设备成功数量", requiredMode = Schema.RequiredMode.REQUIRED, example = "66") - private Integer deviceSuccessCount; - - @Schema(description = "创建时间", requiredMode = Schema.RequiredMode.REQUIRED, example = "2022-07-08 07:30:00") - private LocalDateTime createTime; - -} diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/ota/vo/task/record/IotOtaTaskRecordPageReqVO.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/ota/vo/task/record/IotOtaTaskRecordPageReqVO.java deleted file mode 100644 index 00c6fe7..0000000 --- a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/ota/vo/task/record/IotOtaTaskRecordPageReqVO.java +++ /dev/null @@ -1,20 +0,0 @@ -package cn.iocoder.yudao.module.iot.controller.admin.ota.vo.task.record; - -import cn.iocoder.yudao.framework.common.pojo.PageParam; -import cn.iocoder.yudao.framework.common.validation.InEnum; -import cn.iocoder.yudao.module.iot.enums.ota.IotOtaTaskRecordStatusEnum; -import io.swagger.v3.oas.annotations.media.Schema; -import lombok.Data; - -@Schema(description = "管理后台 - IoT OTA 升级记录分页 Request VO") -@Data -public class IotOtaTaskRecordPageReqVO extends PageParam { - - @Schema(description = "升级任务编号", example = "1024") - private Long taskId; - - @Schema(description = "升级记录状态", example = "5") - @InEnum(IotOtaTaskRecordStatusEnum.class) - private Integer status; - -} diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/ota/vo/task/record/IotOtaTaskRecordRespVO.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/ota/vo/task/record/IotOtaTaskRecordRespVO.java deleted file mode 100644 index f7ab1ed..0000000 --- a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/ota/vo/task/record/IotOtaTaskRecordRespVO.java +++ /dev/null @@ -1,48 +0,0 @@ -package cn.iocoder.yudao.module.iot.controller.admin.ota.vo.task.record; - -import io.swagger.v3.oas.annotations.media.Schema; -import lombok.Data; - -import java.time.LocalDateTime; - -@Schema(description = "管理后台 - IoT OTA 升级任务记录 Response VO") -@Data -public class IotOtaTaskRecordRespVO { - - @Schema(description = "编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "1024") - private Long id; - - @Schema(description = "固件编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "1024") - private Long firmwareId; - - @Schema(description = "任务编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "2048") - private Long taskId; - - @Schema(description = "设备编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "1024") - private Long deviceId; - - @Schema(description = "设备名称", example = "智能开关") - private String deviceName; - - @Schema(description = "来源的固件编号", example = "1023") - private Long fromFirmwareId; - - @Schema(description = "来源固件版本", example = "1.0.0") - private String fromFirmwareVersion; - - @Schema(description = "升级状态", requiredMode = Schema.RequiredMode.REQUIRED, example = "1") - private Integer status; - - @Schema(description = "升级进度,百分比", requiredMode = Schema.RequiredMode.REQUIRED, example = "50") - private Integer progress; - - @Schema(description = "升级进度描述", example = "正在下载固件...") - private String description; - - @Schema(description = "创建时间", requiredMode = Schema.RequiredMode.REQUIRED) - private LocalDateTime createTime; - - @Schema(description = "更新时间", requiredMode = Schema.RequiredMode.REQUIRED) - private LocalDateTime updateTime; - -} diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/product/ErpProductUnitController.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/product/ErpProductUnitController.java new file mode 100644 index 0000000..9963345 --- /dev/null +++ b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/product/ErpProductUnitController.java @@ -0,0 +1,128 @@ +package cn.iocoder.yudao.module.iot.controller.admin.product; + +import cn.iocoder.yudao.framework.apilog.core.annotation.ApiAccessLog; +import cn.iocoder.yudao.framework.common.enums.CommonStatusEnum; +import cn.iocoder.yudao.framework.common.pojo.CommonResult; +import cn.iocoder.yudao.framework.common.pojo.PageParam; +import cn.iocoder.yudao.framework.common.pojo.PageResult; +import cn.iocoder.yudao.framework.common.util.object.BeanUtils; +import cn.iocoder.yudao.framework.excel.core.util.ExcelUtils; + +import cn.iocoder.yudao.module.iot.controller.admin.product.vo.unit.ErpProductUnitPageReqVO; +import cn.iocoder.yudao.module.iot.controller.admin.product.vo.unit.ErpProductUnitRespVO; +import cn.iocoder.yudao.module.iot.controller.admin.product.vo.unit.ErpProductUnitSaveReqVO; +import cn.iocoder.yudao.module.iot.dal.dataobject.product.ErpProductUnitDO; +import cn.iocoder.yudao.module.iot.dal.mysql.product.ErpProductUnitMapper; +import cn.iocoder.yudao.module.iot.service.product.ErpProductUnitService; +import io.swagger.v3.oas.annotations.Operation; +import io.swagger.v3.oas.annotations.Parameter; +import io.swagger.v3.oas.annotations.tags.Tag; +import org.springframework.security.access.prepost.PreAuthorize; +import org.springframework.validation.annotation.Validated; +import org.springframework.web.bind.annotation.*; + +import javax.annotation.Resource; +import javax.servlet.http.HttpServletResponse; +import javax.validation.Valid; +import java.io.IOException; +import java.util.List; + +import static cn.iocoder.yudao.framework.apilog.core.enums.OperateTypeEnum.EXPORT; +import static cn.iocoder.yudao.framework.common.pojo.CommonResult.error; +import static cn.iocoder.yudao.framework.common.pojo.CommonResult.success; +import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertList; + +@Tag(name = "管理后台 - ERP 产品单位") +@RestController +@RequestMapping("/erp/product-unit") +@Validated +public class ErpProductUnitController { + + @Resource + private ErpProductUnitService productUnitService; + @Resource + private ErpProductUnitMapper productUnitMapper; + + @PostMapping("/create") + @Operation(summary = "创建产品单位") + @PreAuthorize("@ss.hasPermission('erp:product-unit:create')") + public CommonResult createProductUnit(@Valid @RequestBody ErpProductUnitSaveReqVO createReqVO) { + ErpProductUnitPageReqVO productUnitPageReqVO = new ErpProductUnitPageReqVO(); + productUnitPageReqVO.setName(createReqVO.getName()); + if (!productUnitMapper.selectProductUnitExist(productUnitPageReqVO).getList().isEmpty()) { + return error(400,"名称不能重复"); + } + return success(productUnitService.createProductUnit(createReqVO)); + } + + @PutMapping("/update") + @Operation(summary = "更新产品单位") + @PreAuthorize("@ss.hasPermission('erp:product-unit:update')") + public CommonResult updateProductUnit(@Valid @RequestBody ErpProductUnitSaveReqVO updateReqVO) { + productUnitService.updateProductUnit(updateReqVO); + return success(true); + } + + @DeleteMapping("/delete") + @Operation(summary = "删除产品单位") + @Parameter(name = "id", description = "编号", required = true) + @PreAuthorize("@ss.hasPermission('erp:product-unit:delete')") + public CommonResult deleteProductUnit(@RequestParam("id") Long id) { + productUnitService.deleteProductUnit(id); + return success(true); + } + + @GetMapping("/get") + @Operation(summary = "获得产品单位") + @Parameter(name = "id", description = "编号", required = true, example = "1024") + @PreAuthorize("@ss.hasPermission('erp:product-unit:query')") + public CommonResult getProductUnit(@RequestParam("id") Long id) { + ErpProductUnitDO productUnit = productUnitService.getProductUnit(id); + return success(BeanUtils.toBean(productUnit, ErpProductUnitRespVO.class)); + } + + @GetMapping("/page") + @Operation(summary = "获得产品单位分页") + @PreAuthorize("@ss.hasPermission('erp:product-unit:query')") + public CommonResult> getProductUnitPage(@Valid ErpProductUnitPageReqVO pageReqVO) { + PageResult pageResult = productUnitService.getProductUnitPage(pageReqVO); + return success(BeanUtils.toBean(pageResult, ErpProductUnitRespVO.class)); + } + + @GetMapping("/simple-list") + @Operation(summary = "获得产品单位精简列表", description = "只包含被开启的单位,主要用于前端的下拉选项") + public CommonResult> getProductUnitSimpleList() { + List list = productUnitService.getProductUnitListByStatus(CommonStatusEnum.ENABLE.getStatus()); + return success(convertList(list, unit -> new ErpProductUnitRespVO().setId(unit.getId()).setName(unit.getName()))); + } + + @GetMapping("/export-excel") + @Operation(summary = "导出产品单位 Excel") + @PreAuthorize("@ss.hasPermission('erp:product-unit:export')") + @ApiAccessLog(operateType = EXPORT) + public void exportProductUnitExcel(@Valid ErpProductUnitPageReqVO pageReqVO, + HttpServletResponse response) throws IOException { + pageReqVO.setPageSize(PageParam.PAGE_SIZE_NONE); + List list = productUnitService.getProductUnitPage(pageReqVO).getList(); + // 导出 Excel + ExcelUtils.write(response, "产品单位.xls", "数据", ErpProductUnitRespVO.class, + BeanUtils.toBean(list, ErpProductUnitRespVO.class)); + } + + @GetMapping("/list") + @Operation(summary = "获得产品单位列表") + public CommonResult> getProductUnitList() { + List productUnitDOList = productUnitService.getProductUnitList(); + return success(productUnitDOList); + + } + + @GetMapping("/flag-list") + @Operation(summary = "获得产品主单位列表") + public CommonResult> getProductUnitListByFlag() { + List productUnitDOList = productUnitService.getProductUnitListByFlag(); + return success(productUnitDOList); + + } + +} \ No newline at end of file diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/product/IotProductCategoryController.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/product/IotProductCategoryController.java deleted file mode 100644 index 2f3ee70..0000000 --- a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/product/IotProductCategoryController.java +++ /dev/null @@ -1,86 +0,0 @@ -package cn.iocoder.yudao.module.iot.controller.admin.product; - -import cn.iocoder.yudao.framework.common.enums.CommonStatusEnum; -import cn.iocoder.yudao.framework.common.pojo.CommonResult; -import cn.iocoder.yudao.framework.common.pojo.PageResult; -import cn.iocoder.yudao.framework.common.util.object.BeanUtils; -import cn.iocoder.yudao.module.iot.controller.admin.product.vo.category.IotProductCategoryPageReqVO; -import cn.iocoder.yudao.module.iot.controller.admin.product.vo.category.IotProductCategoryRespVO; -import cn.iocoder.yudao.module.iot.controller.admin.product.vo.category.IotProductCategorySaveReqVO; -import cn.iocoder.yudao.module.iot.dal.dataobject.product.IotProductCategoryDO; -import cn.iocoder.yudao.module.iot.service.product.IotProductCategoryService; -import io.swagger.v3.oas.annotations.Operation; -import io.swagger.v3.oas.annotations.Parameter; -import io.swagger.v3.oas.annotations.tags.Tag; -import org.springframework.security.access.prepost.PreAuthorize; -import org.springframework.validation.annotation.Validated; -import org.springframework.web.bind.annotation.*; - -import javax.annotation.Resource; -import javax.validation.Valid; -import java.util.List; - -import static cn.iocoder.yudao.framework.common.pojo.CommonResult.success; -import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertList; - -@Tag(name = "管理后台 - IoT 产品分类") -@RestController -@RequestMapping("/iot/product-category") -@Validated -public class IotProductCategoryController { - - @Resource - private IotProductCategoryService productCategoryService; - - @PostMapping("/create") - @Operation(summary = "创建产品分类") - @PreAuthorize("@ss.hasPermission('iot:product-category:create')") - public CommonResult createProductCategory(@Valid @RequestBody IotProductCategorySaveReqVO createReqVO) { - return success(productCategoryService.createProductCategory(createReqVO)); - } - - @PutMapping("/update") - @Operation(summary = "更新产品分类") - @PreAuthorize("@ss.hasPermission('iot:product-category:update')") - public CommonResult updateProductCategory(@Valid @RequestBody IotProductCategorySaveReqVO updateReqVO) { - productCategoryService.updateProductCategory(updateReqVO); - return success(true); - } - - @DeleteMapping("/delete") - @Operation(summary = "删除产品分类") - @Parameter(name = "id", description = "编号", required = true) - @PreAuthorize("@ss.hasPermission('iot:product-category:delete')") - public CommonResult deleteProductCategory(@RequestParam("id") Long id) { - productCategoryService.deleteProductCategory(id); - return success(true); - } - - @GetMapping("/get") - @Operation(summary = "获得产品分类") - @Parameter(name = "id", description = "编号", required = true, example = "1024") - @PreAuthorize("@ss.hasPermission('iot:product-category:query')") - public CommonResult getProductCategory(@RequestParam("id") Long id) { - IotProductCategoryDO productCategory = productCategoryService.getProductCategory(id); - return success(BeanUtils.toBean(productCategory, IotProductCategoryRespVO.class)); - } - - @GetMapping("/page") - @Operation(summary = "获得产品分类分页") - @PreAuthorize("@ss.hasPermission('iot:product-category:query')") - public CommonResult> getProductCategoryPage(@Valid IotProductCategoryPageReqVO pageReqVO) { - PageResult pageResult = productCategoryService.getProductCategoryPage(pageReqVO); - return success(BeanUtils.toBean(pageResult, IotProductCategoryRespVO.class)); - } - - @GetMapping("/simple-list") - @Operation(summary = "获得所有产品分类列表") - @PreAuthorize("@ss.hasPermission('iot:product-category:query')") - public CommonResult> getSimpleProductCategoryList() { - List list = productCategoryService.getProductCategoryListByStatus( - CommonStatusEnum.ENABLE.getStatus()); - return success(convertList(list, category -> - new IotProductCategoryRespVO().setId(category.getId()).setName(category.getName()))); - } - -} \ No newline at end of file diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/product/IotProductController.http b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/product/IotProductController.http deleted file mode 100644 index e4e2589..0000000 --- a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/product/IotProductController.http +++ /dev/null @@ -1,5 +0,0 @@ -### 请求 /iot/product/sync-property-table 接口 => 成功 -POST {{baseUrl}}/iot/product/sync-property-table -Content-Type: application/json -tenant-id: {{adminTenantId}} -Authorization: Bearer {{token}} diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/product/IotProductController.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/product/IotProductController.java deleted file mode 100644 index b0c864f..0000000 --- a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/product/IotProductController.java +++ /dev/null @@ -1,163 +0,0 @@ -package cn.iocoder.yudao.module.iot.controller.admin.product; - -import cn.iocoder.yudao.framework.apilog.core.annotation.ApiAccessLog; -import cn.iocoder.yudao.framework.common.pojo.CommonResult; -import cn.iocoder.yudao.framework.common.pojo.PageParam; -import cn.iocoder.yudao.framework.common.pojo.PageResult; -import cn.iocoder.yudao.framework.common.util.collection.MapUtils; -import cn.iocoder.yudao.framework.common.util.object.BeanUtils; -import cn.iocoder.yudao.framework.excel.core.util.ExcelUtils; -import cn.iocoder.yudao.module.iot.controller.admin.product.vo.product.IotProductPageReqVO; -import cn.iocoder.yudao.module.iot.controller.admin.product.vo.product.IotProductRespVO; -import cn.iocoder.yudao.module.iot.controller.admin.product.vo.product.IotProductSaveReqVO; -import cn.iocoder.yudao.module.iot.dal.dataobject.product.IotProductCategoryDO; -import cn.iocoder.yudao.module.iot.dal.dataobject.product.IotProductDO; -import cn.iocoder.yudao.module.iot.service.product.IotProductCategoryService; -import cn.iocoder.yudao.module.iot.service.product.IotProductService; -import io.swagger.v3.oas.annotations.Operation; -import io.swagger.v3.oas.annotations.Parameter; -import io.swagger.v3.oas.annotations.tags.Tag; -import org.springframework.security.access.prepost.PreAuthorize; -import org.springframework.validation.annotation.Validated; -import org.springframework.web.bind.annotation.*; - -import javax.annotation.Resource; -import javax.servlet.http.HttpServletResponse; -import javax.validation.Valid; -import java.io.IOException; -import java.util.List; -import java.util.Map; - -import static cn.iocoder.yudao.framework.apilog.core.enums.OperateTypeEnum.EXPORT; -import static cn.iocoder.yudao.framework.common.pojo.CommonResult.success; -import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertList; - -@Tag(name = "管理后台 - IoT 产品") -@RestController -@RequestMapping("/iot/product") -@Validated -public class IotProductController { - - @Resource - private IotProductService productService; - @Resource - private IotProductCategoryService categoryService; - - @PostMapping("/create") - @Operation(summary = "创建产品") - @PreAuthorize("@ss.hasPermission('iot:product:create')") - public CommonResult createProduct(@Valid @RequestBody IotProductSaveReqVO createReqVO) { - return success(productService.createProduct(createReqVO)); - } - - @PutMapping("/update") - @Operation(summary = "更新产品") - @PreAuthorize("@ss.hasPermission('iot:product:update')") - public CommonResult updateProduct(@Valid @RequestBody IotProductSaveReqVO updateReqVO) { - productService.updateProduct(updateReqVO); - return success(true); - } - - @PutMapping("/update-status") - @Operation(summary = "更新产品状态") - @Parameter(name = "id", description = "编号", required = true) - @Parameter(name = "status", description = "状态", required = true) - @PreAuthorize("@ss.hasPermission('iot:product:update')") - public CommonResult updateProductStatus(@RequestParam("id") Long id, - @RequestParam("status") Integer status) { - productService.updateProductStatus(id, status); - return success(true); - } - - @DeleteMapping("/delete") - @Operation(summary = "删除产品") - @Parameter(name = "id", description = "编号", required = true) - @PreAuthorize("@ss.hasPermission('iot:product:delete')") - public CommonResult deleteProduct(@RequestParam("id") Long id) { - productService.deleteProduct(id); - return success(true); - } - - @GetMapping("/get") - @Operation(summary = "获得产品") - @Parameter(name = "id", description = "编号", required = true, example = "1024") - @PreAuthorize("@ss.hasPermission('iot:product:query')") - public CommonResult getProduct(@RequestParam("id") Long id) { - IotProductDO product = productService.getProduct(id); - if (product == null) { - return success(null); - } - // 拼接数据 - IotProductCategoryDO category = categoryService.getProductCategory(product.getCategoryId()); - return success(BeanUtils.toBean(product, IotProductRespVO.class, bean -> { - if (category != null) { - bean.setCategoryName(category.getName()); - } - })); - } - - @GetMapping("/get-by-key") - @Operation(summary = "通过 ProductKey 获得产品") - @Parameter(name = "productKey", description = "产品Key", required = true, example = "abc123") - @PreAuthorize("@ss.hasPermission('iot:product:query')") - public CommonResult getProductByKey(@RequestParam("productKey") String productKey) { - IotProductDO product = productService.getProductByProductKey(productKey); - if (product == null) { - return success(null); - } - // 拼接数据 - IotProductCategoryDO category = categoryService.getProductCategory(product.getCategoryId()); - return success(BeanUtils.toBean(product, IotProductRespVO.class, bean -> { - if (category != null) { - bean.setCategoryName(category.getName()); - } - })); - } - - @GetMapping("/page") - @Operation(summary = "获得产品分页") - @PreAuthorize("@ss.hasPermission('iot:product:query')") - public CommonResult> getProductPage(@Valid IotProductPageReqVO pageReqVO) { - PageResult pageResult = productService.getProductPage(pageReqVO); - // 拼接数据 - Map categoryMap = categoryService.getProductCategoryMap( - convertList(pageResult.getList(), IotProductDO::getCategoryId)); - return success(BeanUtils.toBean(pageResult, IotProductRespVO.class, bean -> { - MapUtils.findAndThen(categoryMap, bean.getCategoryId(), - category -> bean.setCategoryName(category.getName())); - })); - } - - @GetMapping("/export-excel") - @Operation(summary = "导出产品 Excel") - @PreAuthorize("@ss.hasPermission('iot:product:export')") - @ApiAccessLog(operateType = EXPORT) - public void exportProductExcel(@Valid IotProductPageReqVO exportReqVO, - HttpServletResponse response) throws IOException { - exportReqVO.setPageSize(PageParam.PAGE_SIZE_NONE); - CommonResult> result = getProductPage(exportReqVO); - // 导出 Excel - ExcelUtils.write(response, "产品.xls", "数据", IotProductRespVO.class, - result.getData().getList()); - } - - @PostMapping("/sync-property-table") - @Operation(summary = "同步产品属性表结构到 TDengine") - @PreAuthorize("@ss.hasPermission('iot:product:update')") - public CommonResult syncProductPropertyTable() { - productService.syncProductPropertyTable(); - return success(true); - } - - @GetMapping("/simple-list") - @Operation(summary = "获取产品的精简信息列表", description = "主要用于前端的下拉选项") - @Parameter(name = "deviceType", description = "设备类型", example = "1") - public CommonResult> getProductSimpleList( - @RequestParam(value = "deviceType", required = false) Integer deviceType) { - List list = productService.getProductList(deviceType); - return success(convertList(list, product -> // 只返回 id、name、productKey 字段 - new IotProductRespVO().setId(product.getId()).setName(product.getName()).setStatus(product.getStatus()) - .setDeviceType(product.getDeviceType()).setProductKey(product.getProductKey()))); - } - -} \ No newline at end of file diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/product/vo/category/IotProductCategoryPageReqVO.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/product/vo/category/IotProductCategoryPageReqVO.java deleted file mode 100644 index f1c12bf..0000000 --- a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/product/vo/category/IotProductCategoryPageReqVO.java +++ /dev/null @@ -1,23 +0,0 @@ -package cn.iocoder.yudao.module.iot.controller.admin.product.vo.category; - -import cn.iocoder.yudao.framework.common.pojo.PageParam; -import io.swagger.v3.oas.annotations.media.Schema; -import lombok.Data; -import org.springframework.format.annotation.DateTimeFormat; - -import java.time.LocalDateTime; - -import static cn.iocoder.yudao.framework.common.util.date.DateUtils.FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND; - -@Schema(description = "管理后台 - IoT 产品分类分页 Request VO") -@Data -public class IotProductCategoryPageReqVO extends PageParam { - - @Schema(description = "分类名字", example = "王五") - private String name; - - @Schema(description = "创建时间") - @DateTimeFormat(pattern = FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND) - private LocalDateTime[] createTime; - -} \ No newline at end of file diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/product/vo/category/IotProductCategoryRespVO.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/product/vo/category/IotProductCategoryRespVO.java deleted file mode 100644 index d684b02..0000000 --- a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/product/vo/category/IotProductCategoryRespVO.java +++ /dev/null @@ -1,33 +0,0 @@ -package cn.iocoder.yudao.module.iot.controller.admin.product.vo.category; - -import cn.iocoder.yudao.framework.excel.core.annotations.DictFormat; -import cn.iocoder.yudao.module.system.enums.DictTypeConstants; -import io.swagger.v3.oas.annotations.media.Schema; -import lombok.Data; - -import java.time.LocalDateTime; - -@Schema(description = "管理后台 - IoT 产品分类 Response VO") -@Data -public class IotProductCategoryRespVO { - - @Schema(description = "分类 ID", requiredMode = Schema.RequiredMode.REQUIRED, example = "25284") - private Long id; - - @Schema(description = "分类名字", requiredMode = Schema.RequiredMode.REQUIRED, example = "王五") - private String name; - - @Schema(description = "分类排序") - private Integer sort; - - @Schema(description = "分类状态", requiredMode = Schema.RequiredMode.REQUIRED, example = "1") - @DictFormat(DictTypeConstants.COMMON_STATUS) - private Integer status; - - @Schema(description = "分类描述", example = "随便") - private String description; - - @Schema(description = "创建时间", requiredMode = Schema.RequiredMode.REQUIRED) - private LocalDateTime createTime; - -} \ No newline at end of file diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/product/vo/category/IotProductCategorySaveReqVO.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/product/vo/category/IotProductCategorySaveReqVO.java deleted file mode 100644 index fc26ca3..0000000 --- a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/product/vo/category/IotProductCategorySaveReqVO.java +++ /dev/null @@ -1,30 +0,0 @@ -package cn.iocoder.yudao.module.iot.controller.admin.product.vo.category; - -import io.swagger.v3.oas.annotations.media.Schema; -import lombok.Data; - -import javax.validation.constraints.NotEmpty; -import javax.validation.constraints.NotNull; - -@Schema(description = "管理后台 - IoT 产品分类新增/修改 Request VO") -@Data -public class IotProductCategorySaveReqVO { - - @Schema(description = "分类 ID", requiredMode = Schema.RequiredMode.REQUIRED, example = "25284") - private Long id; - - @Schema(description = "分类名字", requiredMode = Schema.RequiredMode.REQUIRED, example = "王五") - @NotEmpty(message = "分类名字不能为空") - private String name; - - @Schema(description = "分类排序") - private Integer sort; - - @Schema(description = "分类状态", requiredMode = Schema.RequiredMode.REQUIRED, example = "1") - @NotNull(message = "分类状态不能为空") - private Integer status; - - @Schema(description = "分类描述", example = "随便") - private String description; - -} \ No newline at end of file diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/product/vo/product/IotProductPageReqVO.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/product/vo/product/IotProductPageReqVO.java deleted file mode 100644 index 18c69c4..0000000 --- a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/product/vo/product/IotProductPageReqVO.java +++ /dev/null @@ -1,19 +0,0 @@ -package cn.iocoder.yudao.module.iot.controller.admin.product.vo.product; - -import cn.iocoder.yudao.framework.common.pojo.PageParam; -import io.swagger.v3.oas.annotations.media.Schema; -import lombok.Data; -import lombok.EqualsAndHashCode; -import lombok.ToString; - -@Schema(description = "管理后台 - IoT 产品分页 Request VO") -@Data -public class IotProductPageReqVO extends PageParam { - - @Schema(description = "产品名称", example = "李四") - private String name; - - @Schema(description = "产品标识") - private String productKey; - -} \ No newline at end of file diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/product/vo/product/IotProductRespVO.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/product/vo/product/IotProductRespVO.java deleted file mode 100644 index 302b072..0000000 --- a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/product/vo/product/IotProductRespVO.java +++ /dev/null @@ -1,84 +0,0 @@ -package cn.iocoder.yudao.module.iot.controller.admin.product.vo.product; - -import cn.iocoder.yudao.framework.excel.core.annotations.DictFormat; -import cn.iocoder.yudao.framework.excel.core.convert.DictConvert; -import cn.iocoder.yudao.module.iot.enums.DictTypeConstants; -import cn.idev.excel.annotation.ExcelIgnoreUnannotated; -import cn.idev.excel.annotation.ExcelProperty; -import io.swagger.v3.oas.annotations.media.Schema; -import lombok.Data; - -import java.time.LocalDateTime; - -@Schema(description = "管理后台 - IoT 产品 Response VO") -@Data -@ExcelIgnoreUnannotated -public class IotProductRespVO { - - @Schema(description = "产品编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "26087") - @ExcelProperty("产品编号") - private Long id; - - @Schema(description = "产品名称", requiredMode = Schema.RequiredMode.REQUIRED, example = "李四") - @ExcelProperty("产品名称") - private String name; - - @Schema(description = "产品标识", requiredMode = Schema.RequiredMode.REQUIRED) - @ExcelProperty("产品标识") - private String productKey; - - @Schema(description = "产品密钥", requiredMode = Schema.RequiredMode.REQUIRED) - private String productSecret; - - @Schema(description = "是否开启动态注册", requiredMode = Schema.RequiredMode.REQUIRED, example = "true") - private Boolean registerEnabled; - - @Schema(description = "产品分类编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "1") - private Long categoryId; - - @Schema(description = "产品分类名称", requiredMode = Schema.RequiredMode.REQUIRED, example = "1") - @ExcelProperty("产品分类") - private String categoryName; - - @Schema(description = "产品图标", example = "https://iocoder.cn/1.svg") - @ExcelProperty("产品图标") - private String icon; - - @Schema(description = "产品图片", example = "https://iocoder.cn/1.png") - @ExcelProperty("产品图片") - private String picUrl; - - @Schema(description = "产品描述", example = "你猜") - @ExcelProperty("产品描述") - private String description; - - @Schema(description = "产品状态", requiredMode = Schema.RequiredMode.REQUIRED, example = "1") - @ExcelProperty(value = "产品状态", converter = DictConvert.class) - @DictFormat(DictTypeConstants.PRODUCT_STATUS) - private Integer status; - - @Schema(description = "设备类型", requiredMode = Schema.RequiredMode.REQUIRED, example = "2") - @ExcelProperty(value = "设备类型", converter = DictConvert.class) - @DictFormat(DictTypeConstants.PRODUCT_DEVICE_TYPE) - private Integer deviceType; - - @Schema(description = "联网方式", example = "2") - @ExcelProperty(value = "联网方式", converter = DictConvert.class) - @DictFormat(DictTypeConstants.NET_TYPE) - private Integer netType; - - @Schema(description = "协议类型", requiredMode = Schema.RequiredMode.REQUIRED, example = "mqtt") - @ExcelProperty(value = "协议类型", converter = DictConvert.class) - @DictFormat(DictTypeConstants.PROTOCOL_TYPE) - private String protocolType; - - @Schema(description = "序列化类型", requiredMode = Schema.RequiredMode.REQUIRED, example = "json") - @ExcelProperty(value = "序列化类型", converter = DictConvert.class) - @DictFormat(DictTypeConstants.SERIALIZE_TYPE) - private String serializeType; - - @Schema(description = "创建时间", requiredMode = Schema.RequiredMode.REQUIRED) - @ExcelProperty("创建时间") - private LocalDateTime createTime; - -} diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/product/vo/product/IotProductSaveReqVO.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/product/vo/product/IotProductSaveReqVO.java deleted file mode 100644 index df6ddcd..0000000 --- a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/product/vo/product/IotProductSaveReqVO.java +++ /dev/null @@ -1,64 +0,0 @@ -package cn.iocoder.yudao.module.iot.controller.admin.product.vo.product; - -import cn.iocoder.yudao.framework.common.validation.InEnum; -import cn.iocoder.yudao.module.iot.core.enums.IotProtocolTypeEnum; -import cn.iocoder.yudao.module.iot.core.enums.IotSerializeTypeEnum; -import cn.iocoder.yudao.module.iot.enums.product.IotNetTypeEnum; -import cn.iocoder.yudao.module.iot.enums.product.IotProductDeviceTypeEnum; -import io.swagger.v3.oas.annotations.media.Schema; -import lombok.Data; - -import javax.validation.constraints.NotEmpty; -import javax.validation.constraints.NotNull; - -@Schema(description = "管理后台 - IoT 产品新增/修改 Request VO") -@Data -public class IotProductSaveReqVO { - - @Schema(description = "产品编号", requiredMode = Schema.RequiredMode.AUTO, example = "1") - private Long id; - - @Schema(description = "产品名称", requiredMode = Schema.RequiredMode.REQUIRED, example = "温湿度") - @NotEmpty(message = "产品名称不能为空") - private String name; - - @Schema(description = "产品 Key", requiredMode = Schema.RequiredMode.AUTO, example = "12345abc") - private String productKey; - - @Schema(description = "产品分类编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "1") - @NotNull(message = "产品分类编号不能为空") - private Long categoryId; - - @Schema(description = "产品图标", example = "https://iocoder.cn/1.svg") - private String icon; - - @Schema(description = "产品图片", example = "https://iocoder.cn/1.png") - private String picUrl; - - @Schema(description = "产品描述", example = "描述") - private String description; - - @Schema(description = "设备类型", requiredMode = Schema.RequiredMode.REQUIRED, example = "0") - @InEnum(value = IotProductDeviceTypeEnum.class, message = "设备类型必须是 {value}") - @NotNull(message = "设备类型不能为空") - private Integer deviceType; - - @Schema(description = "联网方式", requiredMode = Schema.RequiredMode.REQUIRED, example = "0") - @InEnum(value = IotNetTypeEnum.class, message = "联网方式必须是 {value}") - private Integer netType; - - @Schema(description = "协议类型", requiredMode = Schema.RequiredMode.REQUIRED, example = "mqtt") - @InEnum(value = IotProtocolTypeEnum.class, message = "协议类型必须是 {value}") - @NotEmpty(message = "协议类型不能为空") - private String protocolType; - - @Schema(description = "序列化类型", requiredMode = Schema.RequiredMode.REQUIRED, example = "json") - @InEnum(value = IotSerializeTypeEnum.class, message = "序列化类型必须是 {value}") - @NotEmpty(message = "序列化类型不能为空") - private String serializeType; - - @Schema(description = "是否开启动态注册", example = "false") - @NotNull(message = "是否开启动态注册不能为空") - private Boolean registerEnabled; - -} \ No newline at end of file diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/product/vo/unit/ErpProductUnitPageReqVO.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/product/vo/unit/ErpProductUnitPageReqVO.java new file mode 100644 index 0000000..fb218f5 --- /dev/null +++ b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/product/vo/unit/ErpProductUnitPageReqVO.java @@ -0,0 +1,21 @@ +package cn.iocoder.yudao.module.iot.controller.admin.product.vo.unit; + +import cn.iocoder.yudao.framework.common.pojo.PageParam; +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.Data; +import lombok.EqualsAndHashCode; +import lombok.ToString; + +@Schema(description = "管理后台 - ERP 产品单位分页 Request VO") +@Data +@EqualsAndHashCode(callSuper = true) +@ToString(callSuper = true) +public class ErpProductUnitPageReqVO extends PageParam { + + @Schema(description = "单位名字", example = "芋艿") + private String name; + + @Schema(description = "单位状态", example = "1") + private Integer status; + +} \ No newline at end of file diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/product/vo/unit/ErpProductUnitRespVO.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/product/vo/unit/ErpProductUnitRespVO.java new file mode 100644 index 0000000..b50b1a2 --- /dev/null +++ b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/product/vo/unit/ErpProductUnitRespVO.java @@ -0,0 +1,46 @@ +package cn.iocoder.yudao.module.iot.controller.admin.product.vo.unit; + +import cn.iocoder.yudao.framework.excel.core.annotations.DictFormat; +import cn.iocoder.yudao.module.system.enums.DictTypeConstants; +import com.alibaba.excel.annotation.ExcelIgnoreUnannotated; +import com.alibaba.excel.annotation.ExcelProperty; +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.Data; + +import java.time.LocalDateTime; + +@Schema(description = "管理后台 - ERP 产品单位 Response VO") +@Data +@ExcelIgnoreUnannotated +public class ErpProductUnitRespVO { + + @Schema(description = "单位编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "31254") + @ExcelProperty("单位编号") + private Long id; + + @Schema(description = "单位名字", requiredMode = Schema.RequiredMode.REQUIRED, example = "芋艿") + @ExcelProperty("单位名字") + private String name; + + @Schema(description = "是否主单位", example = "否") + @ExcelProperty("是否主单位") + private String primaryFlag; + + @Schema(description = "关联主单位id", example = "1") + @ExcelProperty("是否主单位id") + private Long primaryId; + + @Schema(description = "换算比例", example = "1") + @ExcelProperty("换算比例") + private Double changeRate; + + @Schema(description = "单位状态", requiredMode = Schema.RequiredMode.REQUIRED, example = "1") + @ExcelProperty("单位状态") + @DictFormat(DictTypeConstants.COMMON_STATUS) + private Integer status; + + @Schema(description = "创建时间", requiredMode = Schema.RequiredMode.REQUIRED) + @ExcelProperty("创建时间") + private LocalDateTime createTime; + +} \ No newline at end of file diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/product/vo/unit/ErpProductUnitSaveReqVO.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/product/vo/unit/ErpProductUnitSaveReqVO.java new file mode 100644 index 0000000..aeec194 --- /dev/null +++ b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/product/vo/unit/ErpProductUnitSaveReqVO.java @@ -0,0 +1,36 @@ +package cn.iocoder.yudao.module.iot.controller.admin.product.vo.unit; + +import cn.iocoder.yudao.framework.common.enums.CommonStatusEnum; +import cn.iocoder.yudao.framework.common.validation.InEnum; +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.Data; + +import javax.validation.constraints.NotEmpty; +import javax.validation.constraints.NotNull; + +@Schema(description = "管理后台 - ERP 产品单位新增/修改 Request VO") +@Data +public class ErpProductUnitSaveReqVO { + + @Schema(description = "单位编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "31254") + private Long id; + + @Schema(description = "单位名字", requiredMode = Schema.RequiredMode.REQUIRED, example = "芋艿") + @NotEmpty(message = "单位名字不能为空") + private String name; + + @Schema(description = "单位状态", requiredMode = Schema.RequiredMode.REQUIRED, example = "1") + @NotNull(message = "单位状态不能为空") + @InEnum(CommonStatusEnum.class) + private Integer status; + + @Schema(description = "是否主单位", example = "芋艿") + private String primaryFlag; + + @Schema(description = "关联主单位id", example = "芋艿") + private Long primaryId; + + @Schema(description = "换算比例", example = "芋艿") + private Double changeRate; + +} \ No newline at end of file diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/rule/IotDataRuleController.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/rule/IotDataRuleController.java deleted file mode 100644 index 2f7aa7e..0000000 --- a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/rule/IotDataRuleController.java +++ /dev/null @@ -1,73 +0,0 @@ -package cn.iocoder.yudao.module.iot.controller.admin.rule; - -import cn.iocoder.yudao.framework.common.pojo.CommonResult; -import cn.iocoder.yudao.framework.common.pojo.PageResult; -import cn.iocoder.yudao.framework.common.util.object.BeanUtils; -import cn.iocoder.yudao.module.iot.controller.admin.rule.vo.data.rule.IotDataRulePageReqVO; -import cn.iocoder.yudao.module.iot.controller.admin.rule.vo.data.rule.IotDataRuleRespVO; -import cn.iocoder.yudao.module.iot.controller.admin.rule.vo.data.rule.IotDataRuleSaveReqVO; -import cn.iocoder.yudao.module.iot.dal.dataobject.rule.IotDataRuleDO; -import cn.iocoder.yudao.module.iot.service.rule.data.IotDataRuleService; -import io.swagger.v3.oas.annotations.Operation; -import io.swagger.v3.oas.annotations.Parameter; -import io.swagger.v3.oas.annotations.tags.Tag; -import org.springframework.security.access.prepost.PreAuthorize; -import org.springframework.validation.annotation.Validated; -import org.springframework.web.bind.annotation.*; - -import javax.annotation.Resource; -import javax.validation.Valid; - -import static cn.iocoder.yudao.framework.common.pojo.CommonResult.success; - -@Tag(name = "管理后台 - IoT 数据流转规则") -@RestController -@RequestMapping("/iot/data-rule") -@Validated -public class IotDataRuleController { - - @Resource - private IotDataRuleService dataRuleService; - - @PostMapping("/create") - @Operation(summary = "创建数据流转规则") - @PreAuthorize("@ss.hasPermission('iot:data-rule:create')") - public CommonResult createDataRule(@Valid @RequestBody IotDataRuleSaveReqVO createReqVO) { - return success(dataRuleService.createDataRule(createReqVO)); - } - - @PutMapping("/update") - @Operation(summary = "更新数据流转规则") - @PreAuthorize("@ss.hasPermission('iot:data-rule:update')") - public CommonResult updateDataRule(@Valid @RequestBody IotDataRuleSaveReqVO updateReqVO) { - dataRuleService.updateDataRule(updateReqVO); - return success(true); - } - - @DeleteMapping("/delete") - @Operation(summary = "删除数据流转规则") - @Parameter(name = "id", description = "编号", required = true) - @PreAuthorize("@ss.hasPermission('iot:data-rule:delete')") - public CommonResult deleteDataRule(@RequestParam("id") Long id) { - dataRuleService.deleteDataRule(id); - return success(true); - } - - @GetMapping("/get") - @Operation(summary = "获得数据流转规则") - @Parameter(name = "id", description = "编号", required = true, example = "1024") - @PreAuthorize("@ss.hasPermission('iot:data-rule:query')") - public CommonResult getDataRule(@RequestParam("id") Long id) { - IotDataRuleDO dataRule = dataRuleService.getDataRule(id); - return success(BeanUtils.toBean(dataRule, IotDataRuleRespVO.class)); - } - - @GetMapping("/page") - @Operation(summary = "获得数据流转规则分页") - @PreAuthorize("@ss.hasPermission('iot:data-rule:query')") - public CommonResult> getDataRulePage(@Valid IotDataRulePageReqVO pageReqVO) { - PageResult pageResult = dataRuleService.getDataRulePage(pageReqVO); - return success(BeanUtils.toBean(pageResult, IotDataRuleRespVO.class)); - } - -} \ No newline at end of file diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/rule/IotDataSinkController.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/rule/IotDataSinkController.java deleted file mode 100644 index b985f2b..0000000 --- a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/rule/IotDataSinkController.java +++ /dev/null @@ -1,84 +0,0 @@ -package cn.iocoder.yudao.module.iot.controller.admin.rule; - -import cn.iocoder.yudao.framework.common.enums.CommonStatusEnum; -import cn.iocoder.yudao.framework.common.pojo.CommonResult; -import cn.iocoder.yudao.framework.common.pojo.PageResult; -import cn.iocoder.yudao.framework.common.util.object.BeanUtils; -import cn.iocoder.yudao.module.iot.controller.admin.rule.vo.data.sink.IotDataSinkPageReqVO; -import cn.iocoder.yudao.module.iot.controller.admin.rule.vo.data.sink.IotDataSinkRespVO; -import cn.iocoder.yudao.module.iot.controller.admin.rule.vo.data.sink.IotDataSinkSaveReqVO; -import cn.iocoder.yudao.module.iot.dal.dataobject.rule.IotDataSinkDO; -import cn.iocoder.yudao.module.iot.service.rule.data.IotDataSinkService; -import io.swagger.v3.oas.annotations.Operation; -import io.swagger.v3.oas.annotations.Parameter; -import io.swagger.v3.oas.annotations.tags.Tag; -import org.springframework.security.access.prepost.PreAuthorize; -import org.springframework.validation.annotation.Validated; -import org.springframework.web.bind.annotation.*; - -import javax.annotation.Resource; -import javax.validation.Valid; -import java.util.List; - -import static cn.iocoder.yudao.framework.common.pojo.CommonResult.success; -import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertList; - -@Tag(name = "管理后台 - IoT 数据流转目的") -@RestController -@RequestMapping("/iot/data-sink") -@Validated -public class IotDataSinkController { - - @Resource - private IotDataSinkService dataSinkService; - - @PostMapping("/create") - @Operation(summary = "创建数据目的") - @PreAuthorize("@ss.hasPermission('iot:data-sink:create')") - public CommonResult createDataSink(@Valid @RequestBody IotDataSinkSaveReqVO createReqVO) { - return success(dataSinkService.createDataSink(createReqVO)); - } - - @PutMapping("/update") - @Operation(summary = "更新数据目的") - @PreAuthorize("@ss.hasPermission('iot:data-sink:update')") - public CommonResult updateDataSink(@Valid @RequestBody IotDataSinkSaveReqVO updateReqVO) { - dataSinkService.updateDataSink(updateReqVO); - return success(true); - } - - @DeleteMapping("/delete") - @Operation(summary = "删除数据目的") - @Parameter(name = "id", description = "编号", required = true) - @PreAuthorize("@ss.hasPermission('iot:data-sink:delete')") - public CommonResult deleteDataSink(@RequestParam("id") Long id) { - dataSinkService.deleteDataSink(id); - return success(true); - } - - @GetMapping("/get") - @Operation(summary = "获得数据目的") - @Parameter(name = "id", description = "编号", required = true, example = "1024") - @PreAuthorize("@ss.hasPermission('iot:data-sink:query')") - public CommonResult getDataSink(@RequestParam("id") Long id) { - IotDataSinkDO sink = dataSinkService.getDataSink(id); - return success(BeanUtils.toBean(sink, IotDataSinkRespVO.class)); - } - - @GetMapping("/page") - @Operation(summary = "获得数据目的分页") - @PreAuthorize("@ss.hasPermission('iot:data-sink:query')") - public CommonResult> getDataSinkPage(@Valid IotDataSinkPageReqVO pageReqVO) { - PageResult pageResult = dataSinkService.getDataSinkPage(pageReqVO); - return success(BeanUtils.toBean(pageResult, IotDataSinkRespVO.class)); - } - - @GetMapping("/simple-list") - @Operation(summary = "获取数据目的的精简信息列表", description = "主要用于前端的下拉选项") - public CommonResult> getDataSinkSimpleList() { - List list = dataSinkService.getDataSinkListByStatus(CommonStatusEnum.ENABLE.getStatus()); - return success(convertList(list, sink -> // 只返回 id、name 字段 - new IotDataSinkRespVO().setId(sink.getId()).setName(sink.getName()))); - } - -} diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/rule/IotSceneRuleController.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/rule/IotSceneRuleController.java deleted file mode 100644 index a5a8a8a..0000000 --- a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/rule/IotSceneRuleController.java +++ /dev/null @@ -1,93 +0,0 @@ -package cn.iocoder.yudao.module.iot.controller.admin.rule; - -import cn.iocoder.yudao.framework.common.enums.CommonStatusEnum; -import cn.iocoder.yudao.framework.common.pojo.CommonResult; -import cn.iocoder.yudao.framework.common.pojo.PageResult; -import cn.iocoder.yudao.framework.common.util.object.BeanUtils; -import cn.iocoder.yudao.module.iot.controller.admin.rule.vo.scene.IotSceneRulePageReqVO; -import cn.iocoder.yudao.module.iot.controller.admin.rule.vo.scene.IotSceneRuleRespVO; -import cn.iocoder.yudao.module.iot.controller.admin.rule.vo.scene.IotSceneRuleSaveReqVO; -import cn.iocoder.yudao.module.iot.controller.admin.rule.vo.scene.IotSceneRuleUpdateStatusReqVO; -import cn.iocoder.yudao.module.iot.dal.dataobject.rule.IotSceneRuleDO; -import cn.iocoder.yudao.module.iot.service.rule.scene.IotSceneRuleService; -import io.swagger.v3.oas.annotations.Operation; -import io.swagger.v3.oas.annotations.Parameter; -import io.swagger.v3.oas.annotations.tags.Tag; -import org.springframework.security.access.prepost.PreAuthorize; -import org.springframework.validation.annotation.Validated; -import org.springframework.web.bind.annotation.*; - -import javax.annotation.Resource; -import javax.validation.Valid; -import java.util.List; - -import static cn.iocoder.yudao.framework.common.pojo.CommonResult.success; -import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertList; - -@Tag(name = "管理后台 - IoT 场景联动") -@RestController -@RequestMapping("/iot/scene-rule") -@Validated -public class IotSceneRuleController { - - @Resource - private IotSceneRuleService sceneRuleService; - - @PostMapping("/create") - @Operation(summary = "创建场景联动") - @PreAuthorize("@ss.hasPermission('iot:scene-rule:create')") - public CommonResult createSceneRule(@Valid @RequestBody IotSceneRuleSaveReqVO createReqVO) { - return success(sceneRuleService.createSceneRule(createReqVO)); - } - - @PutMapping("/update") - @Operation(summary = "更新场景联动") - @PreAuthorize("@ss.hasPermission('iot:scene-rule:update')") - public CommonResult updateSceneRule(@Valid @RequestBody IotSceneRuleSaveReqVO updateReqVO) { - sceneRuleService.updateSceneRule(updateReqVO); - return success(true); - } - - @PutMapping("/update-status") - @Operation(summary = "更新场景联动状态") - @PreAuthorize("@ss.hasPermission('iot:scene-rule:update')") - public CommonResult updateSceneRuleStatus(@Valid @RequestBody IotSceneRuleUpdateStatusReqVO updateReqVO) { - sceneRuleService.updateSceneRuleStatus(updateReqVO.getId(), updateReqVO.getStatus()); - return success(true); - } - - @DeleteMapping("/delete") - @Operation(summary = "删除场景联动") - @Parameter(name = "id", description = "编号", required = true) - @PreAuthorize("@ss.hasPermission('iot:scene-rule:delete')") - public CommonResult deleteSceneRule(@RequestParam("id") Long id) { - sceneRuleService.deleteSceneRule(id); - return success(true); - } - - @GetMapping("/get") - @Operation(summary = "获得场景联动") - @Parameter(name = "id", description = "编号", required = true, example = "1024") - @PreAuthorize("@ss.hasPermission('iot:scene-rule:query')") - public CommonResult getSceneRule(@RequestParam("id") Long id) { - IotSceneRuleDO sceneRule = sceneRuleService.getSceneRule(id); - return success(BeanUtils.toBean(sceneRule, IotSceneRuleRespVO.class)); - } - - @GetMapping("/page") - @Operation(summary = "获得场景联动分页") - @PreAuthorize("@ss.hasPermission('iot:scene-rule:query')") - public CommonResult> getSceneRulePage(@Valid IotSceneRulePageReqVO pageReqVO) { - PageResult pageResult = sceneRuleService.getSceneRulePage(pageReqVO); - return success(BeanUtils.toBean(pageResult, IotSceneRuleRespVO.class)); - } - - @GetMapping("/simple-list") - @Operation(summary = "获取场景联动的精简信息列表", description = "主要用于前端的下拉选项") - public CommonResult> getSceneRuleSimpleList() { - List list = sceneRuleService.getSceneRuleListByStatus(CommonStatusEnum.ENABLE.getStatus()); - return success(convertList(list, scene -> // 只返回 id、name 字段 - new IotSceneRuleRespVO().setId(scene.getId()).setName(scene.getName()))); - } - -} diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/rule/vo/data/package-info.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/rule/vo/data/package-info.java deleted file mode 100644 index 6be90cf..0000000 --- a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/rule/vo/data/package-info.java +++ /dev/null @@ -1 +0,0 @@ -package cn.iocoder.yudao.module.iot.controller.admin.rule.vo.data; \ No newline at end of file diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/rule/vo/data/rule/IotDataRulePageReqVO.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/rule/vo/data/rule/IotDataRulePageReqVO.java deleted file mode 100644 index 8e21c79..0000000 --- a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/rule/vo/data/rule/IotDataRulePageReqVO.java +++ /dev/null @@ -1,26 +0,0 @@ -package cn.iocoder.yudao.module.iot.controller.admin.rule.vo.data.rule; - -import cn.iocoder.yudao.framework.common.pojo.PageParam; -import io.swagger.v3.oas.annotations.media.Schema; -import lombok.Data; -import org.springframework.format.annotation.DateTimeFormat; - -import java.time.LocalDateTime; - -import static cn.iocoder.yudao.framework.common.util.date.DateUtils.FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND; - -@Schema(description = "管理后台 - IoT 数据流转规则分页 Request VO") -@Data -public class IotDataRulePageReqVO extends PageParam { - - @Schema(description = "数据流转规则名称", example = "芋艿") - private String name; - - @Schema(description = "数据流转规则状态", example = "1") - private Integer status; - - @Schema(description = "创建时间") - @DateTimeFormat(pattern = FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND) - private LocalDateTime[] createTime; - -} \ No newline at end of file diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/rule/vo/data/rule/IotDataRuleRespVO.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/rule/vo/data/rule/IotDataRuleRespVO.java deleted file mode 100644 index 3427370..0000000 --- a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/rule/vo/data/rule/IotDataRuleRespVO.java +++ /dev/null @@ -1,35 +0,0 @@ -package cn.iocoder.yudao.module.iot.controller.admin.rule.vo.data.rule; - -import cn.iocoder.yudao.module.iot.dal.dataobject.rule.IotDataRuleDO; -import io.swagger.v3.oas.annotations.media.Schema; -import lombok.Data; - -import java.time.LocalDateTime; -import java.util.List; - -@Schema(description = "管理后台 - IoT 数据流转规则 Response VO") -@Data -public class IotDataRuleRespVO { - - @Schema(description = "数据流转规则编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "8540") - private Long id; - - @Schema(description = "数据流转规则名称", requiredMode = Schema.RequiredMode.REQUIRED, example = "芋艿") - private String name; - - @Schema(description = "数据流转规则描述", example = "你猜") - private String description; - - @Schema(description = "数据流转规则状态", requiredMode = Schema.RequiredMode.REQUIRED, example = "1") - private Integer status; - - @Schema(description = "数据源配置数组", requiredMode = Schema.RequiredMode.REQUIRED) - private List sourceConfigs; - - @Schema(description = "数据目的编号数组", requiredMode = Schema.RequiredMode.REQUIRED) - private List sinkIds; - - @Schema(description = "创建时间", requiredMode = Schema.RequiredMode.REQUIRED) - private LocalDateTime createTime; - -} \ No newline at end of file diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/rule/vo/data/rule/IotDataRuleSaveReqVO.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/rule/vo/data/rule/IotDataRuleSaveReqVO.java deleted file mode 100644 index 12b7056..0000000 --- a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/rule/vo/data/rule/IotDataRuleSaveReqVO.java +++ /dev/null @@ -1,40 +0,0 @@ -package cn.iocoder.yudao.module.iot.controller.admin.rule.vo.data.rule; - -import cn.iocoder.yudao.framework.common.enums.CommonStatusEnum; -import cn.iocoder.yudao.framework.common.validation.InEnum; -import cn.iocoder.yudao.module.iot.dal.dataobject.rule.IotDataRuleDO; -import io.swagger.v3.oas.annotations.media.Schema; -import lombok.Data; - -import javax.validation.constraints.NotEmpty; -import javax.validation.constraints.NotNull; -import java.util.List; - -@Schema(description = "管理后台 - IoT 数据流转规则新增/修改 Request VO") -@Data -public class IotDataRuleSaveReqVO { - - @Schema(description = "数据流转规则编号", example = "8540") - private Long id; - - @Schema(description = "数据流转规则名称", requiredMode = Schema.RequiredMode.REQUIRED, example = "芋艿") - @NotEmpty(message = "数据流转规则名称不能为空") - private String name; - - @Schema(description = "数据流转规则描述", example = "你猜") - private String description; - - @Schema(description = "数据流转规则状态", requiredMode = Schema.RequiredMode.REQUIRED, example = "1") - @NotNull(message = "数据流转规则状态不能为空") - @InEnum(CommonStatusEnum.class) - private Integer status; - - @Schema(description = "数据源配置数组", requiredMode = Schema.RequiredMode.REQUIRED) - @NotEmpty(message = "数据源配置数组不能为空") - private List sourceConfigs; - - @Schema(description = "数据目的编号数组", requiredMode = Schema.RequiredMode.REQUIRED) - @NotEmpty(message = "数据目的编号数组不能为空") - private List sinkIds; - -} \ No newline at end of file diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/rule/vo/data/sink/IotDataSinkPageReqVO.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/rule/vo/data/sink/IotDataSinkPageReqVO.java deleted file mode 100644 index 8a8fcde..0000000 --- a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/rule/vo/data/sink/IotDataSinkPageReqVO.java +++ /dev/null @@ -1,34 +0,0 @@ -package cn.iocoder.yudao.module.iot.controller.admin.rule.vo.data.sink; - -import cn.iocoder.yudao.framework.common.enums.CommonStatusEnum; -import cn.iocoder.yudao.framework.common.pojo.PageParam; -import cn.iocoder.yudao.framework.common.validation.InEnum; -import cn.iocoder.yudao.module.iot.enums.rule.IotDataSinkTypeEnum; -import io.swagger.v3.oas.annotations.media.Schema; -import lombok.Data; -import org.springframework.format.annotation.DateTimeFormat; - -import java.time.LocalDateTime; - -import static cn.iocoder.yudao.framework.common.util.date.DateUtils.FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND; - -@Schema(description = "管理后台 - IoT 数据流转目的分页 Request VO") -@Data -public class IotDataSinkPageReqVO extends PageParam { - - @Schema(description = "数据目的名称", example = "赵六") - private String name; - - @Schema(description = "数据目的状态", example = "2") - @InEnum(CommonStatusEnum.class) - private Integer status; - - @Schema(description = "数据目的类型", example = "1") - @InEnum(IotDataSinkTypeEnum.class) - private Integer type; - - @Schema(description = "创建时间") - @DateTimeFormat(pattern = FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND) - private LocalDateTime[] createTime; - -} \ No newline at end of file diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/rule/vo/data/sink/IotDataSinkRespVO.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/rule/vo/data/sink/IotDataSinkRespVO.java deleted file mode 100644 index 0ced03c..0000000 --- a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/rule/vo/data/sink/IotDataSinkRespVO.java +++ /dev/null @@ -1,34 +0,0 @@ -package cn.iocoder.yudao.module.iot.controller.admin.rule.vo.data.sink; - -import cn.iocoder.yudao.module.iot.dal.dataobject.rule.config.IotAbstractDataSinkConfig; -import io.swagger.v3.oas.annotations.media.Schema; -import lombok.Data; - -import java.time.LocalDateTime; - -@Schema(description = "管理后台 - IoT 数据流转目的 Response VO") -@Data -public class IotDataSinkRespVO { - - @Schema(description = "数据目的编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "18564") - private Long id; - - @Schema(description = "数据目的名称", requiredMode = Schema.RequiredMode.REQUIRED, example = "赵六") - private String name; - - @Schema(description = "数据目的描述", example = "随便") - private String description; - - @Schema(description = "数据目的状态", requiredMode = Schema.RequiredMode.REQUIRED, example = "1") - private Integer status; - - @Schema(description = "数据目的类型", requiredMode = Schema.RequiredMode.REQUIRED, example = "1") - private Integer type; - - @Schema(description = "数据目的配置") - private IotAbstractDataSinkConfig config; - - @Schema(description = "创建时间", requiredMode = Schema.RequiredMode.REQUIRED) - private LocalDateTime createTime; - -} \ No newline at end of file diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/rule/vo/data/sink/IotDataSinkSaveReqVO.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/rule/vo/data/sink/IotDataSinkSaveReqVO.java deleted file mode 100644 index c0d5bc5..0000000 --- a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/rule/vo/data/sink/IotDataSinkSaveReqVO.java +++ /dev/null @@ -1,41 +0,0 @@ -package cn.iocoder.yudao.module.iot.controller.admin.rule.vo.data.sink; - -import cn.iocoder.yudao.framework.common.enums.CommonStatusEnum; -import cn.iocoder.yudao.framework.common.validation.InEnum; -import cn.iocoder.yudao.module.iot.dal.dataobject.rule.config.IotAbstractDataSinkConfig; -import cn.iocoder.yudao.module.iot.enums.rule.IotDataSinkTypeEnum; -import io.swagger.v3.oas.annotations.media.Schema; -import lombok.Data; - -import javax.validation.constraints.NotEmpty; -import javax.validation.constraints.NotNull; - -@Schema(description = "管理后台 - IoT 数据流转目的新增/修改 Request VO") -@Data -public class IotDataSinkSaveReqVO { - - @Schema(description = "数据目的编号", example = "18564") - private Long id; - - @Schema(description = "数据目的名称", requiredMode = Schema.RequiredMode.REQUIRED, example = "赵六") - @NotEmpty(message = "数据目的名称不能为空") - private String name; - - @Schema(description = "数据目的描述", example = "随便") - private String description; - - @Schema(description = "数据目的状态", requiredMode = Schema.RequiredMode.REQUIRED, example = "1") - @NotNull(message = "数据目的状态不能为空") - @InEnum(CommonStatusEnum.class) - private Integer status; - - @Schema(description = "数据目的类型", requiredMode = Schema.RequiredMode.REQUIRED, example = "1") - @NotNull(message = "数据目的类型不能为空") - @InEnum(IotDataSinkTypeEnum.class) - private Integer type; - - @Schema(description = "数据目的配置") - @NotNull(message = "数据目的配置不能为空") - private IotAbstractDataSinkConfig config; - -} \ No newline at end of file diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/rule/vo/scene/IotSceneRuleRespVO.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/rule/vo/scene/IotSceneRuleRespVO.java deleted file mode 100644 index 835ef62..0000000 --- a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/rule/vo/scene/IotSceneRuleRespVO.java +++ /dev/null @@ -1,35 +0,0 @@ -package cn.iocoder.yudao.module.iot.controller.admin.rule.vo.scene; - -import cn.iocoder.yudao.module.iot.dal.dataobject.rule.IotSceneRuleDO; -import io.swagger.v3.oas.annotations.media.Schema; -import lombok.Data; - -import java.time.LocalDateTime; -import java.util.List; - -@Schema(description = "管理后台 - IoT 场景联动 Response VO") -@Data -public class IotSceneRuleRespVO { - - @Schema(description = "场景编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "15865") - private Long id; - - @Schema(description = "场景名称", requiredMode = Schema.RequiredMode.REQUIRED, example = "赵六") - private String name; - - @Schema(description = "场景描述", example = "你猜") - private String description; - - @Schema(description = "场景状态", requiredMode = Schema.RequiredMode.REQUIRED, example = "1") - private Integer status; - - @Schema(description = "触发器数组", requiredMode = Schema.RequiredMode.REQUIRED) - private List triggers; - - @Schema(description = "执行器数组", requiredMode = Schema.RequiredMode.REQUIRED) - private List actions; - - @Schema(description = "创建时间", requiredMode = Schema.RequiredMode.REQUIRED) - private LocalDateTime createTime; - -} \ No newline at end of file diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/rule/vo/scene/IotSceneRuleSaveReqVO.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/rule/vo/scene/IotSceneRuleSaveReqVO.java deleted file mode 100644 index 8b208d6..0000000 --- a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/rule/vo/scene/IotSceneRuleSaveReqVO.java +++ /dev/null @@ -1,40 +0,0 @@ -package cn.iocoder.yudao.module.iot.controller.admin.rule.vo.scene; - -import cn.iocoder.yudao.framework.common.enums.CommonStatusEnum; -import cn.iocoder.yudao.framework.common.validation.InEnum; -import cn.iocoder.yudao.module.iot.dal.dataobject.rule.IotSceneRuleDO; -import io.swagger.v3.oas.annotations.media.Schema; -import lombok.Data; - -import javax.validation.constraints.NotEmpty; -import javax.validation.constraints.NotNull; -import java.util.List; - -@Schema(description = "管理后台 - IoT 场景联动新增/修改 Request VO") -@Data -public class IotSceneRuleSaveReqVO { - - @Schema(description = "场景编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "15865") - private Long id; - - @Schema(description = "场景名称", requiredMode = Schema.RequiredMode.REQUIRED, example = "赵六") - @NotEmpty(message = "场景名称不能为空") - private String name; - - @Schema(description = "场景描述", example = "你猜") - private String description; - - @Schema(description = "场景状态", requiredMode = Schema.RequiredMode.REQUIRED, example = "2") - @NotNull(message = "场景状态不能为空") - @InEnum(CommonStatusEnum.class) - private Integer status; - - @Schema(description = "触发器数组", requiredMode = Schema.RequiredMode.REQUIRED) - @NotEmpty(message = "触发器数组不能为空") - private List triggers; - - @Schema(description = "执行器数组", requiredMode = Schema.RequiredMode.REQUIRED) - @NotEmpty(message = "执行器数组不能为空") - private List actions; - -} \ No newline at end of file diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/rule/vo/scene/IotSceneRuleUpdateStatusReqVO.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/rule/vo/scene/IotSceneRuleUpdateStatusReqVO.java deleted file mode 100644 index 9ba4f81..0000000 --- a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/rule/vo/scene/IotSceneRuleUpdateStatusReqVO.java +++ /dev/null @@ -1,23 +0,0 @@ -package cn.iocoder.yudao.module.iot.controller.admin.rule.vo.scene; - -import cn.iocoder.yudao.framework.common.enums.CommonStatusEnum; -import cn.iocoder.yudao.framework.common.validation.InEnum; -import io.swagger.v3.oas.annotations.media.Schema; -import lombok.Data; - -import javax.validation.constraints.NotNull; - -@Schema(description = "管理后台 - IoT 场景联动更新状态 Request VO") -@Data -public class IotSceneRuleUpdateStatusReqVO { - - @Schema(description = "场景联动编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "1024") - @NotNull(message = "场景联动编号不能为空") - private Long id; - - @Schema(description = "状态", requiredMode = Schema.RequiredMode.REQUIRED, example = "0") - @NotNull(message = "状态不能为空") - @InEnum(value = CommonStatusEnum.class, message = "修改状态必须是 {value}") - private Integer status; - -} diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/statistics/IotStatisticsController.http b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/statistics/IotStatisticsController.http deleted file mode 100644 index b8cb6b5..0000000 --- a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/statistics/IotStatisticsController.http +++ /dev/null @@ -1,11 +0,0 @@ -### 请求 /iot/statistics/get-device-message-summary-by-date 接口(小时) -GET {{baseUrl}}/iot/statistics/get-device-message-summary-by-date?interval=0×[0]=2025-06-13 00:00:00×[1]=2025-06-14 23:59:59 -Content-Type: application/json -tenant-id: {{adminTenantId}} -Authorization: Bearer {{token}} - -### 请求 /iot/statistics/get-device-message-summary-by-date 接口(天) -GET {{baseUrl}}/iot/statistics/get-device-message-summary-by-date?interval=1×[0]=2025-06-13 00:00:00×[1]=2025-06-14 23:59:59 -Content-Type: application/json -tenant-id: {{adminTenantId}} -Authorization: Bearer {{token}} diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/statistics/IotStatisticsController.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/statistics/IotStatisticsController.java deleted file mode 100644 index 0e3be4a..0000000 --- a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/statistics/IotStatisticsController.java +++ /dev/null @@ -1,77 +0,0 @@ -package cn.iocoder.yudao.module.iot.controller.admin.statistics; - -import cn.iocoder.yudao.framework.common.pojo.CommonResult; -import cn.iocoder.yudao.framework.common.util.date.LocalDateTimeUtils; -import cn.iocoder.yudao.module.iot.controller.admin.statistics.vo.IotStatisticsDeviceMessageReqVO; -import cn.iocoder.yudao.module.iot.controller.admin.statistics.vo.IotStatisticsDeviceMessageSummaryByDateRespVO; -import cn.iocoder.yudao.module.iot.controller.admin.statistics.vo.IotStatisticsSummaryRespVO; -import cn.iocoder.yudao.module.iot.core.enums.device.IotDeviceStateEnum; -import cn.iocoder.yudao.module.iot.service.device.IotDeviceService; -import cn.iocoder.yudao.module.iot.service.device.message.IotDeviceMessageService; -import cn.iocoder.yudao.module.iot.service.product.IotProductCategoryService; -import cn.iocoder.yudao.module.iot.service.product.IotProductService; -import io.swagger.v3.oas.annotations.Operation; -import io.swagger.v3.oas.annotations.tags.Tag; -import org.springframework.validation.annotation.Validated; -import org.springframework.web.bind.annotation.GetMapping; -import org.springframework.web.bind.annotation.RequestMapping; -import org.springframework.web.bind.annotation.RestController; - -import javax.annotation.Resource; -import javax.validation.Valid; -import java.time.LocalDateTime; -import java.util.List; -import java.util.Map; - -import static cn.iocoder.yudao.framework.common.pojo.CommonResult.success; - -@Tag(name = "管理后台 - IoT 数据统计") -@RestController -@RequestMapping("/iot/statistics") -@Validated -public class IotStatisticsController { - - @Resource - private IotDeviceService deviceService; - @Resource - private IotProductCategoryService productCategoryService; - @Resource - private IotProductService productService; - @Resource - private IotDeviceMessageService deviceMessageService; - - @GetMapping("/get-summary") - @Operation(summary = "获取全局的数据统计") - public CommonResult getStatisticsSummary(){ - IotStatisticsSummaryRespVO respVO = new IotStatisticsSummaryRespVO(); - // 1.1 获取总数 - respVO.setProductCategoryCount(productCategoryService.getProductCategoryCount(null)); - respVO.setProductCount(productService.getProductCount(null)); - respVO.setDeviceCount(deviceService.getDeviceCount(null)); - respVO.setDeviceMessageCount(deviceMessageService.getDeviceMessageCount(null)); - // 1.2 获取今日新增数量 - LocalDateTime todayStart = LocalDateTimeUtils.getToday(); - respVO.setProductCategoryTodayCount(productCategoryService.getProductCategoryCount(todayStart)); - respVO.setProductTodayCount(productService.getProductCount(todayStart)); - respVO.setDeviceTodayCount(deviceService.getDeviceCount(todayStart)); - respVO.setDeviceMessageTodayCount(deviceMessageService.getDeviceMessageCount(todayStart)); - - // 2. 获取各个品类下设备数量统计 - respVO.setProductCategoryDeviceCounts(productCategoryService.getProductCategoryDeviceCountMap()); - - // 3. 获取设备状态数量统计 - Map deviceCountMap = deviceService.getDeviceCountMapByState(); - respVO.setDeviceOnlineCount(deviceCountMap.getOrDefault(IotDeviceStateEnum.ONLINE.getState(), 0L)); - respVO.setDeviceOfflineCount(deviceCountMap.getOrDefault(IotDeviceStateEnum.OFFLINE.getState(), 0L)); - respVO.setDeviceInactiveCount(deviceCountMap.getOrDefault(IotDeviceStateEnum.INACTIVE.getState(), 0L)); - return success(respVO); - } - - @GetMapping("/get-device-message-summary-by-date") - @Operation(summary = "获取设备消息的数据统计") - public CommonResult> getDeviceMessageSummaryByDate( - @Valid IotStatisticsDeviceMessageReqVO reqVO) { - return success(deviceMessageService.getDeviceMessageSummaryByDate(reqVO)); - } - -} diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/statistics/vo/IotStatisticsDeviceMessageReqVO.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/statistics/vo/IotStatisticsDeviceMessageReqVO.java deleted file mode 100644 index 92c9477..0000000 --- a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/statistics/vo/IotStatisticsDeviceMessageReqVO.java +++ /dev/null @@ -1,27 +0,0 @@ -package cn.iocoder.yudao.module.iot.controller.admin.statistics.vo; - -import cn.iocoder.yudao.framework.common.enums.DateIntervalEnum; -import cn.iocoder.yudao.framework.common.validation.InEnum; -import io.swagger.v3.oas.annotations.media.Schema; -import lombok.Data; -import org.springframework.format.annotation.DateTimeFormat; - -import javax.validation.constraints.Size; -import java.time.LocalDateTime; - -import static cn.iocoder.yudao.framework.common.util.date.DateUtils.FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND; - -@Schema(description = "管理后台 - IoT 设备消息数量统计 Response VO") -@Data -public class IotStatisticsDeviceMessageReqVO { - - @Schema(description = "时间间隔类型", requiredMode = Schema.RequiredMode.REQUIRED, example = "1") - @InEnum(value = DateIntervalEnum.class, message = "时间间隔类型,必须是 {value}") - private Integer interval; - - @Schema(description = "时间范围", requiredMode = Schema.RequiredMode.NOT_REQUIRED) - @DateTimeFormat(pattern = FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND) - @Size(min = 2, max = 2, message = "请选择时间范围") - private LocalDateTime[] times; - -} diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/statistics/vo/IotStatisticsDeviceMessageSummaryByDateRespVO.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/statistics/vo/IotStatisticsDeviceMessageSummaryByDateRespVO.java deleted file mode 100644 index 9c605dd..0000000 --- a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/statistics/vo/IotStatisticsDeviceMessageSummaryByDateRespVO.java +++ /dev/null @@ -1,19 +0,0 @@ -package cn.iocoder.yudao.module.iot.controller.admin.statistics.vo; - -import io.swagger.v3.oas.annotations.media.Schema; -import lombok.Data; - -@Schema(description = "管理后台 - IoT 设备消息数量统计 Response VO") -@Data -public class IotStatisticsDeviceMessageSummaryByDateRespVO { - - @Schema(description = "时间轴", requiredMode = Schema.RequiredMode.REQUIRED, example = "202401") - private String time; - - @Schema(description = "上行消息数量", requiredMode = Schema.RequiredMode.REQUIRED, example = "10") - private Integer upstreamCount; - - @Schema(description = "上行消息数量", requiredMode = Schema.RequiredMode.REQUIRED, example = "20") - private Integer downstreamCount; - -} diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/statistics/vo/IotStatisticsSummaryRespVO.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/statistics/vo/IotStatisticsSummaryRespVO.java deleted file mode 100644 index 21745c4..0000000 --- a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/statistics/vo/IotStatisticsSummaryRespVO.java +++ /dev/null @@ -1,51 +0,0 @@ -package cn.iocoder.yudao.module.iot.controller.admin.statistics.vo; - -import io.swagger.v3.oas.annotations.media.Schema; -import lombok.Data; - -import java.util.Map; - -/** - * 管理后台 - IoT 统计 Response VO - */ -@Schema(description = "管理后台 - IoT 统计 Response VO") -@Data -public class IotStatisticsSummaryRespVO { - - @Schema(description = "品类数量", requiredMode = Schema.RequiredMode.REQUIRED, example = "10") - private Long productCategoryCount; - - @Schema(description = "产品数量", requiredMode = Schema.RequiredMode.REQUIRED, example = "20") - private Long productCount; - - @Schema(description = "设备数量", requiredMode = Schema.RequiredMode.REQUIRED, example = "100") - private Long deviceCount; - - @Schema(description = "上报数量", requiredMode = Schema.RequiredMode.REQUIRED, example = "1000") - private Long deviceMessageCount; - - @Schema(description = "今日新增品类数量", requiredMode = Schema.RequiredMode.REQUIRED, example = "10") - private Long productCategoryTodayCount; - - @Schema(description = "今日新增产品数量", requiredMode = Schema.RequiredMode.REQUIRED, example = "20") - private Long productTodayCount; - - @Schema(description = "今日新增设备数量", requiredMode = Schema.RequiredMode.REQUIRED, example = "100") - private Long deviceTodayCount; - - @Schema(description = "今日新增上报数量", requiredMode = Schema.RequiredMode.REQUIRED, example = "1000") - private Long deviceMessageTodayCount; - - @Schema(description = "在线数量", requiredMode = Schema.RequiredMode.REQUIRED, example = "80") - private Long deviceOnlineCount; - - @Schema(description = "离线数量", requiredMode = Schema.RequiredMode.REQUIRED, example = "15") - private Long deviceOfflineCount; - - @Schema(description = "待激活设备数量", requiredMode = Schema.RequiredMode.REQUIRED, example = "5") - private Long deviceInactiveCount; - - @Schema(description = "按品类统计的设备数量") - private Map productCategoryDeviceCounts; - -} diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/thingmodel/IotThingModelController.http b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/thingmodel/IotThingModelController.http deleted file mode 100644 index 4f579c4..0000000 --- a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/thingmodel/IotThingModelController.http +++ /dev/null @@ -1,180 +0,0 @@ -### 请求 /iot/product-thing-model/create 接口 => 成功 -POST {{baseUrl}}/iot/product-thing-model/create -Content-Type: application/json -tenant-id: {{adminTenentId}} -Authorization: Bearer {{token}} - -{ - "productId": 12, - "productKey": "CJVS54fObwZJ9Qe5CJVS54fObwZJ9Qe5", - "identifier": "Temperature", - "name": "温度", - "description": "当前温度值", - "type": 1, - "property": { - "identifier": "Temperature", - "name": "温度", - "accessMode": "r", - "required": true, - "dataType": "int", - "dataSpecs": { - "dataType": "int", - "max": "200", - "min": "0", - "step": "10", - "defaultValue": "30", - "unit": "%", - "unitName": "百分比" - } - } -} - -### 请求 /iot/product-thing-model/create 接口 => 成功 -POST {{baseUrl}}/iot/product-thing-model/create -Content-Type: application/json -tenant-id: {{adminTenentId}} -Authorization: Bearer {{token}} - -{ - "productId": 12, - "productKey": "CJVS54fObwZJ9Qe5CJVS54fObwZJ9Qe5", - "identifier": "switch", - "name": "开关", - "description": "温度计开关", - "type": 1, - "property": { - "identifier": "switch", - "name": "开关", - "accessMode": "rw", - "required": true, - "dataType": "bool", - "dataSpecsList": [ - { - "dataType": "bool", - "name": "关", - "value": 0 - }, - { - "dataType": "bool", - "name": "开", - "value": 1 - } - ] - } -} - -### 请求 /iot/product-thing-model/create 接口 => 成功 -POST {{baseUrl}}/iot/product-thing-model/create -Content-Type: application/json -tenant-id: {{adminTenentId}} -Authorization: Bearer {{token}} - -{ - "productId": 12, - "productKey": "CJVS54fObwZJ9Qe5CJVS54fObwZJ9Qe5", - "identifier": "argb", - "name": "温度计 argb 颜色", - "description": "温度计 argb 颜色", - "type": 1, - "property": { - "identifier": "argb", - "name": "温度计 argb 颜色", - "accessMode": "rw", - "required": true, - "dataType": "array", - "dataSpecs": { - "dataType": "array", - "size": 10, - "childDataType": "struct", - "dataSpecsList": [ - { - "identifier": "switch", - "name": "开关", - "accessMode": "rw", - "required": true, - "dataType": "struct", - "childDataType": "bool", - "dataSpecsList": [ - { - "dataType": "bool", - "name": "关", - "value": 0 - }, - { - "dataType": "bool", - "name": "开", - "value": 1 - } - ] - }, - { - "identifier": "Temperature", - "name": "温度", - "accessMode": "r", - "required": true, - "dataType": "struct", - "childDataType": "int", - "dataSpecs": { - "dataType": "int", - "max": "200", - "min": "0", - "step": "10", - "defaultValue": "30", - "unit": "%", - "unitName": "百分比" - } - } - ] - } - } -} - -### 请求 /iot/product-thing-model/update 接口 => 成功 -PUT {{baseUrl}}/iot/product-thing-model/update -Content-Type: application/json -tenant-id: {{adminTenentId}} -Authorization: Bearer {{token}} - -{ - "id": 33, - "productId": 12, - "productKey": "CJVS54fObwZJ9Qe5CJVS54fObwZJ9Qe5", - "identifier": "switch", - "name": "开关", - "description": "温度计开关", - "type": 1, - "property": { - "identifier": "switch", - "name": "开关", - "accessMode": "r", - "required": true, - "dataType": "bool", - "dataSpecsList": [ - { - "dataType": "bool", - "name": "关", - "value": 0 - }, - { - "dataType": "bool", - "name": "开", - "value": 1 - } - ] - } -} - -### 请求 /iot/product-thing-model/delete 接口 => 成功 -DELETE {{baseUrl}}/iot/product-thing-model/delete?id=36 -tenant-id: {{adminTenentId}} -Authorization: Bearer {{token}} - -### 请求 /iot/product-thing-model/get 接口 => 成功 -GET {{baseUrl}}/iot/product-thing-model/get?id=67 -tenant-id: {{adminTenentId}} -Authorization: Bearer {{token}} - -### 请求 /iot/product-thing-model/get-tsl 接口 => 成功 -GET {{baseUrl}}/iot/product-thing-model/get-tsl?productId=1001 -tenant-id: {{adminTenentId}} -Authorization: Bearer {{token}} \ No newline at end of file diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/thingmodel/IotThingModelController.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/thingmodel/IotThingModelController.java deleted file mode 100644 index 9863e07..0000000 --- a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/thingmodel/IotThingModelController.java +++ /dev/null @@ -1,111 +0,0 @@ -package cn.iocoder.yudao.module.iot.controller.admin.thingmodel; - -import cn.iocoder.yudao.framework.common.pojo.CommonResult; -import cn.iocoder.yudao.framework.common.pojo.PageResult; -import cn.iocoder.yudao.framework.common.util.object.BeanUtils; -import cn.iocoder.yudao.module.iot.controller.admin.thingmodel.vo.*; -import cn.iocoder.yudao.module.iot.dal.dataobject.product.IotProductDO; -import cn.iocoder.yudao.module.iot.dal.dataobject.thingmodel.IotThingModelDO; -import cn.iocoder.yudao.module.iot.enums.thingmodel.IotThingModelTypeEnum; -import cn.iocoder.yudao.module.iot.service.product.IotProductService; -import cn.iocoder.yudao.module.iot.service.thingmodel.IotThingModelService; -import com.google.common.base.Objects; -import io.swagger.v3.oas.annotations.Operation; -import io.swagger.v3.oas.annotations.Parameter; -import io.swagger.v3.oas.annotations.tags.Tag; -import org.springframework.security.access.prepost.PreAuthorize; -import org.springframework.validation.annotation.Validated; -import org.springframework.web.bind.annotation.*; - -import javax.annotation.Resource; -import javax.validation.Valid; -import java.util.List; - -import static cn.iocoder.yudao.framework.common.pojo.CommonResult.success; -import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertList; -import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.filterList; - -@Tag(name = "管理后台 - IoT 产品物模型") -@RestController -@RequestMapping("/iot/thing-model") -@Validated -public class IotThingModelController { - - @Resource - private IotThingModelService thingModelService; - @Resource - private IotProductService productService; - - @PostMapping("/create") - @Operation(summary = "创建产品物模型") - @PreAuthorize("@ss.hasPermission('iot:thing-model:create')") - public CommonResult createThingModel(@Valid @RequestBody IotThingModelSaveReqVO createReqVO) { - return success(thingModelService.createThingModel(createReqVO)); - } - - @PutMapping("/update") - @Operation(summary = "更新产品物模型") - @PreAuthorize("@ss.hasPermission('iot:thing-model:update')") - public CommonResult updateThingModel(@Valid @RequestBody IotThingModelSaveReqVO updateReqVO) { - thingModelService.updateThingModel(updateReqVO); - return success(true); - } - - @DeleteMapping("/delete") - @Operation(summary = "删除产品物模型") - @Parameter(name = "id", description = "编号", required = true) - @PreAuthorize("@ss.hasPermission('iot:thing-model:delete')") - public CommonResult deleteThingModel(@RequestParam("id") Long id) { - thingModelService.deleteThingModel(id); - return success(true); - } - - @GetMapping("/get") - @Operation(summary = "获得产品物模型") - @Parameter(name = "id", description = "编号", required = true) - @PreAuthorize("@ss.hasPermission('iot:thing-model:query')") - public CommonResult getThingModel(@RequestParam("id") Long id) { - IotThingModelDO thingModel = thingModelService.getThingModel(id); - return success(BeanUtils.toBean(thingModel, IotThingModelRespVO.class)); - } - - @GetMapping("/get-tsl") - @Operation(summary = "获得产品物模型 TSL") - @Parameter(name = "productId", description = "产品 ID", required = true, example = "1024") - @PreAuthorize("@ss.hasPermission('iot:thing-model:query')") - public CommonResult getThingModelTsl(@RequestParam("productId") Long productId) { - // 1. 获得产品 - IotProductDO product = productService.getProduct(productId); - if (product == null) { - return success(null); - } - IotThingModelTSLRespVO tslRespVO = new IotThingModelTSLRespVO() - .setProductId(product.getId()).setProductKey(product.getProductKey()); - // 2. 获得物模型定义 - List thingModels = thingModelService.getThingModelListByProductId(productId); - tslRespVO.setProperties(convertList(filterList(thingModels, item -> - Objects.equal(IotThingModelTypeEnum.PROPERTY.getType(), item.getType())), IotThingModelDO::getProperty)) - .setServices(convertList(filterList(thingModels, item -> - Objects.equal(IotThingModelTypeEnum.SERVICE.getType(), item.getType())), IotThingModelDO::getService)) - .setEvents(convertList(filterList(thingModels, item -> - Objects.equal(IotThingModelTypeEnum.EVENT.getType(), item.getType())), IotThingModelDO::getEvent)); - return success(tslRespVO); - } - - @GetMapping("/list") - @Operation(summary = "获得产品物模型列表") - @PreAuthorize("@ss.hasPermission('iot:thing-model:query')") - public CommonResult> getThingModelListByProductId(@Valid IotThingModelListReqVO reqVO) { - List list = thingModelService.getThingModelList(reqVO); - return success(BeanUtils.toBean(list, IotThingModelRespVO.class)); - } - - @GetMapping("/page") - @Operation(summary = "获得产品物模型分页") - @PreAuthorize("@ss.hasPermission('iot:thing-model:query')") - public CommonResult> getThingModelPage(@Valid IotThingModelPageReqVO pageReqVO) { - PageResult pageResult = thingModelService.getProductThingModelPage(pageReqVO); - return success(BeanUtils.toBean(pageResult, IotThingModelRespVO.class)); - } - -} diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/thingmodel/vo/IotThingModelListReqVO.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/thingmodel/vo/IotThingModelListReqVO.java deleted file mode 100644 index 8a939be..0000000 --- a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/thingmodel/vo/IotThingModelListReqVO.java +++ /dev/null @@ -1,28 +0,0 @@ -package cn.iocoder.yudao.module.iot.controller.admin.thingmodel.vo; - -import cn.iocoder.yudao.framework.common.validation.InEnum; -import cn.iocoder.yudao.module.iot.enums.thingmodel.IotThingModelTypeEnum; -import io.swagger.v3.oas.annotations.media.Schema; -import lombok.Data; - -import javax.validation.constraints.NotNull; - -@Schema(description = "管理后台 - IoT 产品物模型 List Request VO") -@Data -public class IotThingModelListReqVO { - - @Schema(description = "产品编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "1024") - @NotNull(message = "产品编号不能为空") - private Long productId; - - @Schema(description = "功能标识", example = "temperature") - private String identifier; - - @Schema(description = "功能名称", example = "温度") - private String name; - - @Schema(description = "功能类型", example = "1") - @InEnum(IotThingModelTypeEnum.class) - private Integer type; - -} diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/thingmodel/vo/IotThingModelPageReqVO.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/thingmodel/vo/IotThingModelPageReqVO.java deleted file mode 100644 index 18cb42d..0000000 --- a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/thingmodel/vo/IotThingModelPageReqVO.java +++ /dev/null @@ -1,29 +0,0 @@ -package cn.iocoder.yudao.module.iot.controller.admin.thingmodel.vo; - -import cn.iocoder.yudao.framework.common.pojo.PageParam; -import cn.iocoder.yudao.framework.common.validation.InEnum; -import cn.iocoder.yudao.module.iot.enums.thingmodel.IotThingModelTypeEnum; -import io.swagger.v3.oas.annotations.media.Schema; -import lombok.Data; - -import javax.validation.constraints.NotNull; - -@Schema(description = "管理后台 - IoT 产品物模型分页 Request VO") -@Data -public class IotThingModelPageReqVO extends PageParam { - - @Schema(description = "产品编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "1024") - @NotNull(message = "产品编号不能为空") - private Long productId; - - @Schema(description = "功能标识", example = "temperature") - private String identifier; - - @Schema(description = "功能名称", example = "温度") - private String name; - - @Schema(description = "功能类型", example = "1") - @InEnum(IotThingModelTypeEnum.class) - private Integer type; - -} \ No newline at end of file diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/thingmodel/vo/IotThingModelRespVO.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/thingmodel/vo/IotThingModelRespVO.java deleted file mode 100644 index a7a17dd..0000000 --- a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/thingmodel/vo/IotThingModelRespVO.java +++ /dev/null @@ -1,48 +0,0 @@ -package cn.iocoder.yudao.module.iot.controller.admin.thingmodel.vo; - -import cn.iocoder.yudao.module.iot.dal.dataobject.thingmodel.model.ThingModelEvent; -import cn.iocoder.yudao.module.iot.dal.dataobject.thingmodel.model.ThingModelProperty; -import cn.iocoder.yudao.module.iot.dal.dataobject.thingmodel.model.ThingModelService; -import io.swagger.v3.oas.annotations.media.Schema; -import lombok.Data; - -import java.time.LocalDateTime; - -@Schema(description = "管理后台 - IoT 产品物模型 Response VO") -@Data -public class IotThingModelRespVO { - - @Schema(description = "产品编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "21816") - private Long id; - - @Schema(description = "产品标识", requiredMode = Schema.RequiredMode.REQUIRED, example = "1024") - private Long productId; - - @Schema(description = "产品标识", requiredMode = Schema.RequiredMode.REQUIRED, example = "temperature_sensor") - private String productKey; - - @Schema(description = "功能标识", requiredMode = Schema.RequiredMode.REQUIRED, example = "temperature") - private String identifier; - - @Schema(description = "功能名称", requiredMode = Schema.RequiredMode.REQUIRED, example = "温度") - private String name; - - @Schema(description = "功能描述", requiredMode = Schema.RequiredMode.REQUIRED, example = "测量当前环境温度") - private String description; - - @Schema(description = "功能类型", requiredMode = Schema.RequiredMode.REQUIRED, example = "1") - private Integer type; - - @Schema(description = "属性", requiredMode = Schema.RequiredMode.REQUIRED) - private ThingModelProperty property; - - @Schema(description = "服务", requiredMode = Schema.RequiredMode.REQUIRED) - private ThingModelEvent event; - - @Schema(description = "事件", requiredMode = Schema.RequiredMode.REQUIRED) - private ThingModelService service; - - @Schema(description = "创建时间", requiredMode = Schema.RequiredMode.REQUIRED) - private LocalDateTime createTime; - -} \ No newline at end of file diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/thingmodel/vo/IotThingModelSaveReqVO.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/thingmodel/vo/IotThingModelSaveReqVO.java deleted file mode 100644 index 79a6a0a..0000000 --- a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/thingmodel/vo/IotThingModelSaveReqVO.java +++ /dev/null @@ -1,58 +0,0 @@ -package cn.iocoder.yudao.module.iot.controller.admin.thingmodel.vo; - -import cn.iocoder.yudao.framework.common.validation.InEnum; -import cn.iocoder.yudao.module.iot.dal.dataobject.thingmodel.model.ThingModelEvent; -import cn.iocoder.yudao.module.iot.dal.dataobject.thingmodel.model.ThingModelProperty; -import cn.iocoder.yudao.module.iot.dal.dataobject.thingmodel.model.ThingModelService; -import cn.iocoder.yudao.module.iot.enums.thingmodel.IotThingModelTypeEnum; -import io.swagger.v3.oas.annotations.media.Schema; -import lombok.Data; - -import javax.validation.Valid; -import javax.validation.constraints.NotEmpty; -import javax.validation.constraints.NotNull; - -@Schema(description = "管理后台 - IoT 产品物模型新增/修改 Request VO") -@Data -public class IotThingModelSaveReqVO { - - @Schema(description = "编号", example = "1") - private Long id; - - @Schema(description = "产品ID", requiredMode = Schema.RequiredMode.REQUIRED) - @NotNull(message = "产品ID不能为空") - private Long productId; - - @Schema(description = "产品标识", requiredMode = Schema.RequiredMode.REQUIRED, example = "temperature_001") - @NotEmpty(message = "产品标识不能为空") - private String productKey; - - @Schema(description = "功能标识", requiredMode = Schema.RequiredMode.REQUIRED, example = "temp_monitor") - @NotEmpty(message = "功能标识不能为空") - private String identifier; - - @Schema(description = "功能名称", requiredMode = Schema.RequiredMode.REQUIRED, example = "温度监测器") - @NotEmpty(message = "功能名称不能为空") - private String name; - - @Schema(description = "功能描述", requiredMode = Schema.RequiredMode.REQUIRED, example = "用于监测环境温度的传感器") - private String description; - - @Schema(description = "功能类型", requiredMode = Schema.RequiredMode.REQUIRED, example = "1") - @NotNull(message = "功能类型不能为空") - @InEnum(IotThingModelTypeEnum.class) - private Integer type; - - @Schema(description = "属性", requiredMode = Schema.RequiredMode.REQUIRED) - @Valid - private ThingModelProperty property; - - @Schema(description = "服务", requiredMode = Schema.RequiredMode.REQUIRED) - @Valid - private ThingModelService service; - - @Schema(description = "事件", requiredMode = Schema.RequiredMode.REQUIRED) - @Valid - private ThingModelEvent event; - -} \ No newline at end of file diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/thingmodel/vo/IotThingModelTSLRespVO.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/thingmodel/vo/IotThingModelTSLRespVO.java deleted file mode 100644 index d3809d8..0000000 --- a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/thingmodel/vo/IotThingModelTSLRespVO.java +++ /dev/null @@ -1,30 +0,0 @@ -package cn.iocoder.yudao.module.iot.controller.admin.thingmodel.vo; - -import cn.iocoder.yudao.module.iot.dal.dataobject.thingmodel.model.ThingModelEvent; -import cn.iocoder.yudao.module.iot.dal.dataobject.thingmodel.model.ThingModelProperty; -import cn.iocoder.yudao.module.iot.dal.dataobject.thingmodel.model.ThingModelService; -import io.swagger.v3.oas.annotations.media.Schema; -import lombok.Data; - -import java.util.List; - -@Schema(description = "管理后台 - IoT 产品物模型 TSL Response VO") -@Data -public class IotThingModelTSLRespVO { - - @Schema(description = "产品编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "1024") - private Long productId; - - @Schema(description = "产品标识", requiredMode = Schema.RequiredMode.REQUIRED, example = "temperature_sensor") - private String productKey; - - @Schema(description = "属性列表", requiredMode = Schema.RequiredMode.REQUIRED) - private List properties; - - @Schema(description = "服务列表", requiredMode = Schema.RequiredMode.REQUIRED) - private List events; - - @Schema(description = "事件列表", requiredMode = Schema.RequiredMode.REQUIRED) - private List services; - -} \ No newline at end of file diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/convert/thingmodel/IotThingModelConvert.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/convert/thingmodel/IotThingModelConvert.java deleted file mode 100644 index 6abbe97..0000000 --- a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/convert/thingmodel/IotThingModelConvert.java +++ /dev/null @@ -1,50 +0,0 @@ -package cn.iocoder.yudao.module.iot.convert.thingmodel; - -import cn.iocoder.yudao.module.iot.dal.dataobject.thingmodel.model.ThingModelEvent; -import cn.iocoder.yudao.module.iot.dal.dataobject.thingmodel.model.ThingModelProperty; -import cn.iocoder.yudao.module.iot.dal.dataobject.thingmodel.model.ThingModelService; -import cn.iocoder.yudao.module.iot.controller.admin.thingmodel.vo.IotThingModelSaveReqVO; -import cn.iocoder.yudao.module.iot.dal.dataobject.thingmodel.IotThingModelDO; -import cn.iocoder.yudao.module.iot.enums.thingmodel.IotThingModelTypeEnum; -import org.mapstruct.Mapper; -import org.mapstruct.Mapping; -import org.mapstruct.Named; -import org.mapstruct.factory.Mappers; - -import java.util.Objects; - -@Mapper -public interface IotThingModelConvert { - - IotThingModelConvert INSTANCE = Mappers.getMapper(IotThingModelConvert.class); - - @Mapping(target = "property", expression = "java(convertToProperty(bean))") - @Mapping(target = "event", expression = "java(convertToEvent(bean))") - @Mapping(target = "service", expression = "java(convertToService(bean))") - IotThingModelDO convert(IotThingModelSaveReqVO bean); - - @Named("convertToProperty") - default ThingModelProperty convertToProperty(IotThingModelSaveReqVO bean) { - if (Objects.equals(bean.getType(), IotThingModelTypeEnum.PROPERTY.getType())) { - return bean.getProperty(); - } - return null; - } - - @Named("convertToEvent") - default ThingModelEvent convertToEvent(IotThingModelSaveReqVO bean) { - if (Objects.equals(bean.getType(), IotThingModelTypeEnum.EVENT.getType())) { - return bean.getEvent(); - } - return null; - } - - @Named("convertToService") - default ThingModelService convertToService(IotThingModelSaveReqVO bean) { - if (Objects.equals(bean.getType(), IotThingModelTypeEnum.SERVICE.getType())) { - return bean.getService(); - } - return null; - } - -} diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/dataobject/alert/IotAlertConfigDO.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/dataobject/alert/IotAlertConfigDO.java deleted file mode 100644 index 69f466b..0000000 --- a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/dataobject/alert/IotAlertConfigDO.java +++ /dev/null @@ -1,84 +0,0 @@ -package cn.iocoder.yudao.module.iot.dal.dataobject.alert; - -import cn.iocoder.yudao.framework.common.enums.CommonStatusEnum; -import cn.iocoder.yudao.framework.mybatis.core.dataobject.BaseDO; -import cn.iocoder.yudao.framework.mybatis.core.type.IntegerListTypeHandler; -import cn.iocoder.yudao.framework.mybatis.core.type.LongListTypeHandler; -import cn.iocoder.yudao.module.iot.dal.dataobject.rule.IotSceneRuleDO; -import cn.iocoder.yudao.module.iot.enums.DictTypeConstants; -import cn.iocoder.yudao.module.iot.enums.alert.IotAlertReceiveTypeEnum; -import cn.iocoder.yudao.module.system.api.user.dto.AdminUserRespDTO; -import com.baomidou.mybatisplus.annotation.KeySequence; -import com.baomidou.mybatisplus.annotation.TableField; -import com.baomidou.mybatisplus.annotation.TableId; -import com.baomidou.mybatisplus.annotation.TableName; -import lombok.AllArgsConstructor; -import lombok.Builder; -import lombok.Data; -import lombok.NoArgsConstructor; - -import java.util.List; - -/** - * IoT 告警配置 DO - * - * @author 芋道源码 - */ -@TableName(value = "iot_alert_config", autoResultMap = true) -@KeySequence("iot_alert_config_seq") // 用于 Oracle、PostgreSQL、Kingbase、DB2、H2 数据库的主键自增。如果是 MySQL 等数据库,可不写。 -@Data -@Builder -@NoArgsConstructor -@AllArgsConstructor -public class IotAlertConfigDO extends BaseDO { - - /** - * 配置编号 - */ - @TableId - private Long id; - /** - * 配置名称 - */ - private String name; - /** - * 配置描述 - */ - private String description; - /** - * 配置状态 - * - * 字典 {@link DictTypeConstants#ALERT_LEVEL} - */ - private Integer level; - /** - * 配置状态 - * - * 枚举 {@link CommonStatusEnum} - */ - private Integer status; - - /** - * 关联的场景联动规则编号数组 - * - * 关联 {@link IotSceneRuleDO#getId()} - */ - @TableField(typeHandler = LongListTypeHandler.class) - private List sceneRuleIds; - - /** - * 接收的用户编号数组 - * - * 关联 {@link AdminUserRespDTO#getId()} - */ - @TableField(typeHandler = LongListTypeHandler.class) - private List receiveUserIds; - /** - * 接收的类型数组 - * - * 枚举 {@link IotAlertReceiveTypeEnum} - */ - @TableField(typeHandler = IntegerListTypeHandler.class) - private List receiveTypes; - -} diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/dataobject/alert/IotAlertRecordDO.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/dataobject/alert/IotAlertRecordDO.java deleted file mode 100644 index 29b1c7d..0000000 --- a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/dataobject/alert/IotAlertRecordDO.java +++ /dev/null @@ -1,89 +0,0 @@ -package cn.iocoder.yudao.module.iot.dal.dataobject.alert; - -import cn.iocoder.yudao.framework.mybatis.core.dataobject.BaseDO; -import cn.iocoder.yudao.module.iot.core.mq.message.IotDeviceMessage; -import cn.iocoder.yudao.module.iot.dal.dataobject.device.IotDeviceDO; -import cn.iocoder.yudao.module.iot.dal.dataobject.product.IotProductDO; -import cn.iocoder.yudao.module.iot.dal.dataobject.rule.IotSceneRuleDO; -import com.baomidou.mybatisplus.annotation.KeySequence; -import com.baomidou.mybatisplus.annotation.TableField; -import com.baomidou.mybatisplus.annotation.TableId; -import com.baomidou.mybatisplus.annotation.TableName; -import com.baomidou.mybatisplus.extension.handlers.JacksonTypeHandler; -import lombok.AllArgsConstructor; -import lombok.Builder; -import lombok.Data; -import lombok.NoArgsConstructor; - -/** - * IoT 告警记录 DO - * - * @author 芋道源码 - */ -@TableName(value = "iot_alert_record", autoResultMap = true) -@KeySequence("iot_alert_record_seq") // 用于 Oracle、PostgreSQL、Kingbase、DB2、H2 数据库的主键自增。如果是 MySQL 等数据库,可不写。 -@Data -@Builder -@NoArgsConstructor -@AllArgsConstructor -public class IotAlertRecordDO extends BaseDO { - - /** - * 记录编号 - */ - @TableId - private Long id; - /** - * 告警名称 - * - * 冗余 {@link IotAlertConfigDO#getId()} - */ - private Long configId; - /** - * 告警名称 - * - * 冗余 {@link IotAlertConfigDO#getName()} - */ - private String configName; - /** - * 告警级别 - * - * 冗余 {@link IotAlertConfigDO#getLevel()} - * 字典 {@link cn.iocoder.yudao.module.iot.enums.DictTypeConstants#ALERT_LEVEL} - */ - private Integer configLevel; - /** - * 场景规则编号 - * - * 关联 {@link IotSceneRuleDO#getId()} - */ - private Long sceneRuleId; - - /** - * 产品编号 - * - * 关联 {@link IotProductDO#getId()} - */ - private Long productId; - /** - * 设备编号 - * - * 关联 {@link IotDeviceDO#getId()} - */ - private Long deviceId; - /** - * 触发的设备消息 - */ - @TableField(typeHandler = JacksonTypeHandler.class) - private IotDeviceMessage deviceMessage; - - /** - * 是否处理 - */ - private Boolean processStatus; - /** - * 处理结果(备注) - */ - private String processRemark; - -} diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/dataobject/device/DeviceAttributeDO.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/dataobject/device/DeviceAttributeDO.java new file mode 100644 index 0000000..b09433b --- /dev/null +++ b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/dataobject/device/DeviceAttributeDO.java @@ -0,0 +1,99 @@ +package cn.iocoder.yudao.module.iot.dal.dataobject.device; + +import cn.iocoder.yudao.framework.mybatis.core.dataobject.BaseDO; +import com.baomidou.mybatisplus.annotation.KeySequence; +import com.baomidou.mybatisplus.annotation.TableId; +import com.baomidou.mybatisplus.annotation.TableName; +import com.sun.xml.bind.v2.TODO; +import lombok.*; + +/** + * 设备属性 DO + * + * @author 内蒙必硕 + */ +@TableName("iot_device_attribute") +@KeySequence("iot_device_attribute_seq") // 用于 Oracle、PostgreSQL、Kingbase、DB2、H2 数据库的主键自增。如果是 MySQL 等数据库,可不写。 +@Data +@EqualsAndHashCode(callSuper = true) +@ToString(callSuper = true) +@Builder +@NoArgsConstructor +@AllArgsConstructor +public class DeviceAttributeDO extends BaseDO { + + /** + * ID + */ + @TableId + private Long id; + /** + * 属性编码 + */ + private String attributeCode; + /** + * 属性名称 + */ + private String attributeName; + /** + * PLC点位地址 + */ + private String address; + /** + * 地址描述 + */ + private String description; + /** + * 地址类型 + * + * 枚举 {@link TODO mes_data_type 对应的类} + */ + private String attributeType; + /** + * 读写方式 + * + * 枚举 {@link TODO iot_attribute_io_type 对应的类} + */ + private String ioType; + /** + * 数据类型 + * + * 枚举 {@link TODO iot_device_data_type 对应的类} + */ + private String dataType; + /** + * 类型描述 + */ + private String dataTypeRemark; + /** + * 单位 + */ + private String dataUnit; + /** + * 数据计算公式 + */ + private String dataFormula; + /** + * 网关id + */ + private Long gatewayId; + /** + * 设备id + */ + private Long deviceId; + /** + * 告警id + */ + private Long alertId; + /** + * 备注 + */ + private String remark; + /** + * 是否启用 + * + * 枚举 {@link TODO infra_boolean_string 对应的类} + */ + private Boolean isEnable; + +} \ No newline at end of file diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/dataobject/device/DeviceDO.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/dataobject/device/DeviceDO.java new file mode 100644 index 0000000..4ef2377 --- /dev/null +++ b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/dataobject/device/DeviceDO.java @@ -0,0 +1,129 @@ +package cn.iocoder.yudao.module.iot.dal.dataobject.device; + +import cn.iocoder.yudao.framework.mybatis.core.dataobject.BaseDO; +import com.baomidou.mybatisplus.annotation.KeySequence; +import com.baomidou.mybatisplus.annotation.TableId; +import com.baomidou.mybatisplus.annotation.TableName; +import lombok.*; + +import java.time.LocalDateTime; + +/** + * 物联设备 DO + * + * @author 内蒙必硕 + */ +@TableName("iot_device") +@KeySequence("iot_device_seq") // 用于 Oracle、PostgreSQL、Kingbase、DB2、H2 数据库的主键自增。如果是 MySQL 等数据库,可不写。 +@Data +@EqualsAndHashCode(callSuper = true) +@ToString(callSuper = true) +@Builder +@NoArgsConstructor +@AllArgsConstructor +public class DeviceDO extends BaseDO { + + /** + * ID + */ + @TableId + private Long id; + /** + * 设备编号 + */ + private String deviceCode; + /** + * 设备名称 + */ + private String deviceName; + /** + * 设备类型 + * + * 枚举 {@link TODO iot_device_type 对应的类} + */ + private String deviceType; + /** + * 状态 + * + * 枚举 {@link TODO iot_gateway_status 对应的类} + */ + private String status; + /** + * 读主题 + */ + private String readTopic; + /** + * 写主题 + */ + private String writeTopic; + /** + * 网关id + */ + private Long gatewayId; + /** + * 设备品牌id + */ + private Long deviceBrandId; + /** + * 离线间隔 + */ + private Long offLineDuration; + /** + * 最后上线时间 + */ + private LocalDateTime lastOnlineTime; + /** + * 备注 + */ + private String remark; + /** + * 是否启用 + * + * 枚举 {@link TODO infra_boolean_string 对应的类} + */ + private Boolean isEnable; + /** + * 关联设备模型 + */ + private Long deviceModelId; + /** + * 通讯协议 + */ + private String protocol; + /** + * 采集周期 + */ + private Double sampleCycle; + /** + * 端点url + */ + private String url; + /** + * 用户名 + */ + private String username; + /** + * 密码 + */ + private String password; + /** + * 租户id + */ + private String tenantId; + + /** + * 订阅主题 + */ + private String topic; + + /** + * 客户Id + */ + private Long customerId; + + /** + * 客户组织节点Id + */ + private Long orgNodeId; + +} \ No newline at end of file diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/dataobject/device/IotDeviceDO.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/dataobject/device/IotDeviceDO.java deleted file mode 100644 index 0499ac0..0000000 --- a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/dataobject/device/IotDeviceDO.java +++ /dev/null @@ -1,139 +0,0 @@ -package cn.iocoder.yudao.module.iot.dal.dataobject.device; - -import cn.iocoder.yudao.framework.mybatis.core.type.LongSetTypeHandler; -import cn.iocoder.yudao.framework.tenant.core.db.TenantBaseDO; -import cn.iocoder.yudao.module.iot.dal.dataobject.ota.IotOtaFirmwareDO; -import cn.iocoder.yudao.module.iot.dal.dataobject.product.IotProductDO; -import cn.iocoder.yudao.module.iot.core.enums.device.IotDeviceStateEnum; -import com.baomidou.mybatisplus.annotation.KeySequence; -import com.baomidou.mybatisplus.annotation.TableField; -import com.baomidou.mybatisplus.annotation.TableId; -import com.baomidou.mybatisplus.annotation.TableName; -import lombok.*; - -import java.math.BigDecimal; -import java.time.LocalDateTime; -import java.util.Set; - -/** - * IoT 设备 DO - * - * @author haohao - */ -@TableName(value = "iot_device", autoResultMap = true) -@KeySequence("iot_device_seq") // 用于 Oracle、PostgreSQL、Kingbase、DB2、H2 数据库的主键自增。如果是 MySQL 等数据库,可不写。 -@Data -@Builder -@NoArgsConstructor -@AllArgsConstructor -public class IotDeviceDO extends TenantBaseDO { - - /** - * 设备编号 - 全部设备 - */ - public static final Long DEVICE_ID_ALL = 0L; - - /** - * 设备 ID,主键,自增 - */ - @TableId - private Long id; - /** - * 设备名称,在产品内唯一,用于标识设备 - */ - private String deviceName; - /** - * 设备备注名称 - */ - private String nickname; - /** - * 设备序列号 - */ - private String serialNumber; - /** - * 设备图片 - */ - private String picUrl; - /** - * 设备分组编号集合 - * - * 关联 {@link IotDeviceGroupDO#getId()} - */ - @TableField(typeHandler = LongSetTypeHandler.class) - private Set groupIds; - - /** - * 产品编号 - *

- * 关联 {@link IotProductDO#getId()} - */ - private Long productId; - /** - * 产品标识 - *

- * 冗余 {@link IotProductDO#getProductKey()} - */ - private String productKey; - /** - * 设备类型 - *

- * 冗余 {@link IotProductDO#getDeviceType()} - */ - private Integer deviceType; - /** - * 网关设备编号 - *

- * 子设备需要关联的网关设备 ID - *

- * 关联 {@link IotDeviceDO#getId()} - */ - private Long gatewayId; - - /** - * 设备状态 - *

- * 枚举 {@link IotDeviceStateEnum} - */ - private Integer state; - /** - * 最后上线时间 - */ - private LocalDateTime onlineTime; - /** - * 最后离线时间 - */ - private LocalDateTime offlineTime; - /** - * 设备激活时间 - */ - private LocalDateTime activeTime; - - /** - * 固件编号 - * - * 关联 {@link IotOtaFirmwareDO#getId()} - */ - private Long firmwareId; - - /** - * 设备密钥,用于设备认证 - */ - private String deviceSecret; - - /** - * 设备位置的纬度 - */ - private BigDecimal latitude; - /** - * 设备位置的经度 - */ - private BigDecimal longitude; - - /** - * 设备配置 - * - * JSON 格式,可下发给 device 进行自定义配置 - */ - private String config; - -} \ No newline at end of file diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/dataobject/device/IotDeviceMessageDO.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/dataobject/device/IotDeviceMessageDO.java deleted file mode 100644 index 233b2c1..0000000 --- a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/dataobject/device/IotDeviceMessageDO.java +++ /dev/null @@ -1,109 +0,0 @@ -package cn.iocoder.yudao.module.iot.dal.dataobject.device; - -import cn.iocoder.yudao.module.iot.core.enums.IotDeviceMessageMethodEnum; -import cn.iocoder.yudao.module.iot.core.mq.message.IotDeviceMessage; -import cn.iocoder.yudao.module.iot.dal.dataobject.thingmodel.IotThingModelDO; -import lombok.AllArgsConstructor; -import lombok.Builder; -import lombok.Data; -import lombok.NoArgsConstructor; - -/** - * IoT 设备消息数据 DO - * - * 目前使用 TDengine 存储 - * - * @author alwayssuper - */ -@Data -@Builder -@NoArgsConstructor -@AllArgsConstructor -public class IotDeviceMessageDO { - - /** - * 消息编号 - */ - private String id; - /** - * 上报时间戳 - */ - private Long reportTime; - /** - * 存储时间戳 - */ - private Long ts; - - /** - * 设备编号 - * - * 关联 {@link IotDeviceDO#getId()} - */ - private Long deviceId; - /** - * 租户编号 - */ - private Long tenantId; - - /** - * 服务编号,该消息由哪个 server 发送 - */ - private String serverId; - - /** - * 是否上行消息 - * - * 由 {@link cn.iocoder.yudao.module.iot.core.util.IotDeviceMessageUtils#isUpstreamMessage(IotDeviceMessage)} 计算。 - * 计算并存储的目的:方便计算多少条上行、多少条下行 - */ - private Boolean upstream; - /** - * 是否回复消息 - * - * 由 {@link cn.iocoder.yudao.module.iot.core.util.IotDeviceMessageUtils#isReplyMessage(IotDeviceMessage)} 计算。 - * 计算并存储的目的:方便计算多少条请求、多少条回复 - */ - private Boolean reply; - /** - * 标识符 - * - * 例如说:{@link IotThingModelDO#getIdentifier()} - * 目前,只有事件上报、服务调用才有!!! - */ - private String identifier; - - // ========== codec(编解码)字段 ========== - - /** - * 请求编号 - * - * 由设备生成,对应阿里云 IoT 的 Alink 协议中的 id、华为云 IoTDA 协议的 request_id - */ - private String requestId; - /** - * 请求方法 - * - * 枚举 {@link IotDeviceMessageMethodEnum} - * 例如说:thing.property.post 属性上报 - */ - private String method; - /** - * 请求参数 - * - * 例如说:属性上报的 properties、事件上报的 params - */ - private Object params; - /** - * 响应结果 - */ - private Object data; - /** - * 响应错误码 - */ - private Integer code; - /** - * 响应提示 - */ - private String msg; - -} diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/dataobject/device/IotDeviceModbusConfigDO.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/dataobject/device/IotDeviceModbusConfigDO.java deleted file mode 100644 index 2698150..0000000 --- a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/dataobject/device/IotDeviceModbusConfigDO.java +++ /dev/null @@ -1,82 +0,0 @@ -package cn.iocoder.yudao.module.iot.dal.dataobject.device; - -import cn.iocoder.yudao.framework.tenant.core.db.TenantBaseDO; -import cn.iocoder.yudao.module.iot.core.enums.modbus.IotModbusFrameFormatEnum; -import cn.iocoder.yudao.module.iot.core.enums.modbus.IotModbusModeEnum; -import cn.iocoder.yudao.module.iot.dal.dataobject.product.IotProductDO; -import com.baomidou.mybatisplus.annotation.KeySequence; -import com.baomidou.mybatisplus.annotation.TableId; -import com.baomidou.mybatisplus.annotation.TableName; -import lombok.*; - -/** - * IoT 设备 Modbus 连接配置 DO - * - * @author 芋道源码 - */ -@TableName("iot_device_modbus_config") -@KeySequence("iot_device_modbus_config_seq") -@Data -@Builder -@NoArgsConstructor -@AllArgsConstructor -public class IotDeviceModbusConfigDO extends TenantBaseDO { - - /** - * 主键 - */ - @TableId - private Long id; - /** - * 产品编号 - * - * 关联 {@link IotProductDO#getId()} - */ - private Long productId; - /** - * 设备编号 - * - * 关联 {@link IotDeviceDO#getId()} - */ - private Long deviceId; - - /** - * Modbus 服务器 IP 地址 - */ - private String ip; - /** - * Modbus 服务器端口 - */ - private Integer port; - /** - * 从站地址 - */ - private Integer slaveId; - /** - * 连接超时时间,单位:毫秒 - */ - private Integer timeout; - /** - * 重试间隔,单位:毫秒 - */ - private Integer retryInterval; - /** - * 模式 - * - * @see IotModbusModeEnum - */ - private Integer mode; - /** - * 数据帧格式 - * - * @see IotModbusFrameFormatEnum - */ - private Integer frameFormat; - /** - * 状态 - * - * 枚举 {@link cn.iocoder.yudao.framework.common.enums.CommonStatusEnum} - */ - private Integer status; - -} diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/dataobject/device/IotDeviceModbusPointDO.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/dataobject/device/IotDeviceModbusPointDO.java deleted file mode 100644 index cfc0841..0000000 --- a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/dataobject/device/IotDeviceModbusPointDO.java +++ /dev/null @@ -1,100 +0,0 @@ -package cn.iocoder.yudao.module.iot.dal.dataobject.device; - -import cn.iocoder.yudao.framework.tenant.core.db.TenantBaseDO; -import cn.iocoder.yudao.module.iot.dal.dataobject.thingmodel.IotThingModelDO; -import cn.iocoder.yudao.module.iot.core.enums.modbus.IotModbusByteOrderEnum; -import cn.iocoder.yudao.module.iot.core.enums.modbus.IotModbusRawDataTypeEnum; -import com.baomidou.mybatisplus.annotation.KeySequence; -import com.baomidou.mybatisplus.annotation.TableId; -import com.baomidou.mybatisplus.annotation.TableName; -import lombok.*; - -import java.math.BigDecimal; - -/** - * IoT 设备 Modbus 点位配置 DO - * - * @author 芋道源码 - */ -@TableName("iot_device_modbus_point") -@KeySequence("iot_device_modbus_point_seq") -@Data -@Builder -@NoArgsConstructor -@AllArgsConstructor -public class IotDeviceModbusPointDO extends TenantBaseDO { - - /** - * 主键 - */ - @TableId - private Long id; - /** - * 设备编号 - * - * 关联 {@link IotDeviceDO#getId()} - */ - private Long deviceId; - /** - * 物模型属性编号 - * - * 关联 {@link IotThingModelDO#getId()} - */ - private Long thingModelId; - /** - * 属性标识符 - * - * 冗余 {@link IotThingModelDO#getIdentifier()} - */ - private String identifier; - /** - * 属性名称 - * - * 冗余 {@link IotThingModelDO#getName()} - */ - private String name; - - // ========== Modbus 协议配置 ========== - - /** - * Modbus 功能码 - * - * 取值范围:FC01-04(读线圈、读离散输入、读保持寄存器、读输入寄存器) - */ - private Integer functionCode; - /** - * 寄存器起始地址 - */ - private Integer registerAddress; - /** - * 寄存器数量 - */ - private Integer registerCount; - /** - * 字节序 - * - * 枚举 {@link IotModbusByteOrderEnum} - */ - private String byteOrder; - /** - * 原始数据类型 - * - * 枚举 {@link IotModbusRawDataTypeEnum} - */ - private String rawDataType; - /** - * 缩放因子 - */ - private BigDecimal scale; - /** - * 轮询间隔(毫秒) - */ - private Integer pollInterval; - /** - * 状态 - * - * 枚举 {@link cn.iocoder.yudao.framework.common.enums.CommonStatusEnum} - */ - private Integer status; - -} diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/dataobject/device/IotDevicePropertyDO.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/dataobject/device/IotDevicePropertyDO.java deleted file mode 100644 index afb3288..0000000 --- a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/dataobject/device/IotDevicePropertyDO.java +++ /dev/null @@ -1,35 +0,0 @@ -package cn.iocoder.yudao.module.iot.dal.dataobject.device; - -import cn.iocoder.yudao.module.iot.dal.redis.device.DevicePropertyRedisDAO; -import lombok.AllArgsConstructor; -import lombok.Builder; -import lombok.Data; -import lombok.NoArgsConstructor; - -import java.time.LocalDateTime; - -/** - * IoT 设备属性项 Redis DO - * - * @see cn.iocoder.yudao.module.iot.dal.redis.RedisKeyConstants#DEVICE_PROPERTY - * @see DevicePropertyRedisDAO - * - * @author haohao - */ -@Data -@Builder -@NoArgsConstructor -@AllArgsConstructor -public class IotDevicePropertyDO { - - /** - * 属性值(最新) - */ - private Object value; - - /** - * 更新时间 - */ - private LocalDateTime updateTime; - -} \ No newline at end of file diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/dataobject/deviceattributetype/DeviceAttributeTypeDO.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/dataobject/deviceattributetype/DeviceAttributeTypeDO.java new file mode 100644 index 0000000..7886cb0 --- /dev/null +++ b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/dataobject/deviceattributetype/DeviceAttributeTypeDO.java @@ -0,0 +1,46 @@ +package cn.iocoder.yudao.module.iot.dal.dataobject.deviceattributetype; + +import cn.iocoder.yudao.framework.mybatis.core.dataobject.BaseDO; +import com.baomidou.mybatisplus.annotation.KeySequence; +import com.baomidou.mybatisplus.annotation.TableId; +import com.baomidou.mybatisplus.annotation.TableName; +import lombok.*; + +/** + * 采集点分类 DO + * + * @author 内蒙必硕 + */ +@TableName("iot_device_attribute_type") +@KeySequence("iot_device_attribute_type_seq") // 用于 Oracle、PostgreSQL、Kingbase、DB2、H2 数据库的主键自增。如果是 MySQL 等数据库,可不写。 +@Data +@EqualsAndHashCode(callSuper = true) +@ToString(callSuper = true) +@Builder +@NoArgsConstructor +@AllArgsConstructor +public class DeviceAttributeTypeDO extends BaseDO { + + /** + * ID + */ + @TableId + private Long id; + /** + * 分类编码 + */ + private String code; + /** + * 分类名称 + */ + private String name; + /** + * 备注 + */ + private String remark; + /** + * 排序 + */ + private int sort; + +} \ No newline at end of file diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/dataobject/devicecontactmodel/DeviceContactModelDO.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/dataobject/devicecontactmodel/DeviceContactModelDO.java new file mode 100644 index 0000000..ea6e572 --- /dev/null +++ b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/dataobject/devicecontactmodel/DeviceContactModelDO.java @@ -0,0 +1,104 @@ +package cn.iocoder.yudao.module.iot.dal.dataobject.devicecontactmodel; + +import cn.iocoder.yudao.framework.mybatis.core.dataobject.BaseDO; +import com.baomidou.mybatisplus.annotation.KeySequence; +import com.baomidou.mybatisplus.annotation.TableField; +import com.baomidou.mybatisplus.annotation.TableId; +import com.baomidou.mybatisplus.annotation.TableName; +import com.fasterxml.jackson.annotation.JsonIgnore; +import lombok.*; + +import java.time.LocalDateTime; + +/** + * 采集设备模型-点位管理 DO + * + * @author 内蒙必硕 + */ +@TableName("iot_device_contact_model") +@KeySequence("iot_device_contact_model_seq") // 用于 Oracle、PostgreSQL、Kingbase、DB2、H2 数据库的主键自增。如果是 MySQL 等数据库,可不写。 +@Data +@EqualsAndHashCode(callSuper = true) +@ToString(callSuper = true) +@Builder +@NoArgsConstructor +@AllArgsConstructor +public class DeviceContactModelDO extends BaseDO { + + /** + * ID + */ + @TableId + private Long id; + /** + * 点位编码 + */ + private String attributeCode; + /** + * 点位名称 + */ + private String attributeName; + /** + * 点位类型 + */ + private String attributeType; + /** + * 数据类型 + */ + private String dataType; + /** + * 寄存器地址 + */ + private String address; + /** + * 单位 + */ + private String dataUnit; + /** + * 倍率 + */ + private Double ratio; + /** + * 备注 + */ + private String remark; + /** + * 采集设备模型id + */ + private Long deviceId; + /** + * 排序 + */ + private int sort; + + /** + * 最新值 + */ + @TableField(exist = false) + private Object addressValue; + + /** + * 最新值 + */ + @TableField(exist = false) + private String latestCollectionTime; + + /** + * 创建时间 - 忽略序列化和反序列化 + */ + @JsonIgnore + private LocalDateTime createTime; + + /** + * 更新时间 - 忽略序列化和反序列化 + */ + @JsonIgnore + private LocalDateTime updateTime; + + + /** + * 采集点类型名称 + */ + private String typeName; + +} \ No newline at end of file diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/dataobject/devicemodel/DeviceModelDO.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/dataobject/devicemodel/DeviceModelDO.java new file mode 100644 index 0000000..3cfe2eb --- /dev/null +++ b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/dataobject/devicemodel/DeviceModelDO.java @@ -0,0 +1,46 @@ +package cn.iocoder.yudao.module.iot.dal.dataobject.devicemodel; + +import cn.iocoder.yudao.framework.mybatis.core.dataobject.BaseDO; +import com.baomidou.mybatisplus.annotation.KeySequence; +import com.baomidou.mybatisplus.annotation.TableId; +import com.baomidou.mybatisplus.annotation.TableName; +import lombok.*; + +/** + * 采集设备模型 DO + * + * @author 内蒙必硕 + */ +@TableName("iot_device_model") +@KeySequence("iot_device_model_seq") // 用于 Oracle、PostgreSQL、Kingbase、DB2、H2 数据库的主键自增。如果是 MySQL 等数据库,可不写。 +@Data +@EqualsAndHashCode(callSuper = true) +@ToString(callSuper = true) +@Builder +@NoArgsConstructor +@AllArgsConstructor +public class DeviceModelDO extends BaseDO { + + /** + * ID + */ + @TableId + private Long id; + /** + * 分类编码 + */ + private String code; + /** + * 分类名称 + */ + private String name; + /** + * 通讯协议 + */ + private String protocol; + /** + * 备注 + */ + private String remark; + +} \ No newline at end of file diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/dataobject/devicemodelattribute/DeviceModelAttributeDO.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/dataobject/devicemodelattribute/DeviceModelAttributeDO.java new file mode 100644 index 0000000..57e7d1f --- /dev/null +++ b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/dataobject/devicemodelattribute/DeviceModelAttributeDO.java @@ -0,0 +1,81 @@ +package cn.iocoder.yudao.module.iot.dal.dataobject.devicemodelattribute; + +import cn.iocoder.yudao.framework.mybatis.core.dataobject.BaseDO; +import com.baomidou.mybatisplus.annotation.KeySequence; +import com.baomidou.mybatisplus.annotation.TableId; +import com.baomidou.mybatisplus.annotation.TableName; +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.*; + +/** + * 采集设备模型-点位管理 DO + * + * @author 内蒙必硕 + */ +@TableName("iot_device_model_attribute") +@KeySequence("iot_device_model_attribute_seq") // 用于 Oracle、PostgreSQL、Kingbase、DB2、H2 数据库的主键自增。如果是 MySQL 等数据库,可不写。 +@Data +@EqualsAndHashCode(callSuper = true) +@ToString(callSuper = true) +@Builder +@NoArgsConstructor +@AllArgsConstructor +public class DeviceModelAttributeDO extends BaseDO { + + /** + * + */ + @TableId + private Long id; + /** + * 点位编码 + */ + @Schema(description = "点位编码") + private String attributeCode; + /** + * 点位名称 + */ + @Schema(description = "点位名称") + private String attributeName; + /** + * 点位类型 + */ + @Schema(description = "点位类型") + private String attributeType; + /** + * 数据类型 + */ + @Schema(description = "数据类型") + private String dataType; + /** + * 寄存器地址 + */ + @Schema(description = "寄存器地址") + private String address; + /** + * 单位 + */ + @Schema(description = "单位") + private String dataUnit; + /** + * 倍率 + */ + @Schema(description = "倍率") + private Double ratio; + /** + * 备注 + */ + @Schema(description = "备注") + private String remark; + /** + * 采集设备模型id + */ + @Schema(description = "采集设备模型id") + private Long deviceModelId; + + /** + * 采集点类型名称 + */ + private String typeName; + +} \ No newline at end of file diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/dataobject/devicemodelrules/DeviceModelRulesDO.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/dataobject/devicemodelrules/DeviceModelRulesDO.java new file mode 100644 index 0000000..25d203b --- /dev/null +++ b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/dataobject/devicemodelrules/DeviceModelRulesDO.java @@ -0,0 +1,55 @@ +package cn.iocoder.yudao.module.iot.dal.dataobject.devicemodelrules; + +import cn.iocoder.yudao.framework.mybatis.core.dataobject.BaseDO; +import com.baomidou.mybatisplus.annotation.KeySequence; +import com.baomidou.mybatisplus.annotation.TableId; +import com.baomidou.mybatisplus.annotation.TableName; +import lombok.*; + +/** + * 点位规则关联模型 DO + * + * @author 必硕科技 + */ +@TableName("iot_device_model_rules") +@KeySequence("iot_device_model_rules_seq") // 用于 Oracle、PostgreSQL、Kingbase、DB2、H2 数据库的主键自增。如果是 MySQL 等数据库,可不写。 +@Data +@EqualsAndHashCode(callSuper = true) +@ToString(callSuper = true) +@Builder +@NoArgsConstructor +@AllArgsConstructor +public class DeviceModelRulesDO extends BaseDO { + + /** + * ID + */ + @TableId + private Long id; + /** + * 标识符 + */ + private String identifier; + /** + * 名称 + */ + private String fieldName; + /** + * 规则 + */ + private String fieldRule; + /** + * 默认值 + */ + private String defaultValue; + /** + * 关联设备模型Id + */ + private Long modelId; + + /** + * 告警等级 + */ + private String alarmLevel; + +} \ No newline at end of file diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/dataobject/deviceoperationrecord/DeviceOperationRecordDO.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/dataobject/deviceoperationrecord/DeviceOperationRecordDO.java new file mode 100644 index 0000000..762a34d --- /dev/null +++ b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/dataobject/deviceoperationrecord/DeviceOperationRecordDO.java @@ -0,0 +1,78 @@ +package cn.iocoder.yudao.module.iot.dal.dataobject.deviceoperationrecord; + +import lombok.*; +import com.baomidou.mybatisplus.annotation.*; +import cn.iocoder.yudao.framework.mybatis.core.dataobject.BaseDO; + +/** + * 运行记录 DO + * + * @author 必硕科技 + */ +@TableName("iot_device_operation_record") +@KeySequence("iot_device_operation_record_seq") // 用于 Oracle、PostgreSQL、Kingbase、DB2、H2 数据库的主键自增。如果是 MySQL 等数据库,可不写。 +@Data +@EqualsAndHashCode(callSuper = true) +@ToString(callSuper = true) +@Builder +@NoArgsConstructor +@AllArgsConstructor +public class DeviceOperationRecordDO extends BaseDO { + + /** + * ID + */ + @TableId + private Long id; + /** + * 设备ID + */ + private Long deviceId; + /** + * 模型ID + */ + private Long modelId; + /** + * 规则值 + */ + private String rule; + /** + * 地址值 + */ + private String addressValue; + /** + * 类型 1-RUNNING 2-ALRAM + */ + private Integer recordType; + + /** + * 规则Id + */ + private Long ruleId; + + /** + * 运行总时长 + */ + private Double totalRunningTime; + + /** + * 待机总时长 + */ + private Double totalStandbyTime; + + /** + * 故障总时长 + */ + private Double totalFaultTime; + + /** + * 报警总时长 + */ + private Double totalWarningTime; + + /** + * 租户id + */ + private String tenantId; + +} \ No newline at end of file diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/dataobject/devicepointrules/DevicePointRulesDO.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/dataobject/devicepointrules/DevicePointRulesDO.java new file mode 100644 index 0000000..fb536e5 --- /dev/null +++ b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/dataobject/devicepointrules/DevicePointRulesDO.java @@ -0,0 +1,55 @@ +package cn.iocoder.yudao.module.iot.dal.dataobject.devicepointrules; + +import cn.iocoder.yudao.framework.mybatis.core.dataobject.BaseDO; +import com.baomidou.mybatisplus.annotation.KeySequence; +import com.baomidou.mybatisplus.annotation.TableId; +import com.baomidou.mybatisplus.annotation.TableName; +import lombok.*; + +/** + * 点位规则 DO + * + * @author 必硕科技 + */ +@TableName("iot_device_point_rules") +@KeySequence("iot_device_point_rules_seq") // 用于 Oracle、PostgreSQL、Kingbase、DB2、H2 数据库的主键自增。如果是 MySQL 等数据库,可不写。 +@Data +@EqualsAndHashCode(callSuper = true) +@ToString(callSuper = true) +@Builder +@NoArgsConstructor +@AllArgsConstructor +public class DevicePointRulesDO extends BaseDO { + + /** + * ID + */ + @TableId + private Long id; + /** + * 标识符 + */ + private String identifier; + /** + * 名称 + */ + private String fieldName; + /** + * 规则 + */ + private String fieldRule; + /** + * 默认值 + */ + private String defaultValue; + /** + * 设备模型Id + */ + private Long deviceId; + + /** + * 告警等级 + */ + private String alarmLevel; + +} \ No newline at end of file diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/dataobject/devicewarinningrecord/DeviceWarinningRecordDO.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/dataobject/devicewarinningrecord/DeviceWarinningRecordDO.java new file mode 100644 index 0000000..1dc0166 --- /dev/null +++ b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/dataobject/devicewarinningrecord/DeviceWarinningRecordDO.java @@ -0,0 +1,67 @@ +package cn.iocoder.yudao.module.iot.dal.dataobject.devicewarinningrecord; + +import cn.iocoder.yudao.framework.mybatis.core.dataobject.BaseDO; +import com.baomidou.mybatisplus.annotation.KeySequence; +import com.baomidou.mybatisplus.annotation.TableId; +import com.baomidou.mybatisplus.annotation.TableName; +import lombok.*; + +/** + * 告警记录 DO + * + * @author 必硕科技 + */ +@TableName("iot_device_warinning_record") +@KeySequence("iot_device_warinning_record_seq") // 用于 Oracle、PostgreSQL、Kingbase、DB2、H2 数据库的主键自增。如果是 MySQL 等数据库,可不写。 +@Data +@EqualsAndHashCode(callSuper = true) +@ToString(callSuper = true) +@Builder +@NoArgsConstructor +@AllArgsConstructor +public class DeviceWarinningRecordDO extends BaseDO { + + /** + * ID + */ + @TableId + private Long id; + /** + * 设备ID + */ + private Long deviceId; + /** + * 模型ID + */ + private Long modelId; + /** + * 规则值 + */ + private String rule; + /** + * 告警等级 + */ + private String alarmLevel; + /** + * 地址值 + */ + private String addressValue; + /** + * 点位规则Id + */ + private Long ruleId; + + /** + * 设备名称 + */ + private String deviceName; + /** + * 点位名称 + */ + private String modelName; + /** + * 点位规则名称 + */ + private String ruleName; + +} \ No newline at end of file diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/dataobject/gateway/GatewayDO.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/dataobject/gateway/GatewayDO.java new file mode 100644 index 0000000..ca262e0 --- /dev/null +++ b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/dataobject/gateway/GatewayDO.java @@ -0,0 +1,68 @@ +package cn.iocoder.yudao.module.iot.dal.dataobject.gateway; + +import cn.iocoder.yudao.framework.mybatis.core.dataobject.BaseDO; +import com.baomidou.mybatisplus.annotation.KeySequence; +import com.baomidou.mybatisplus.annotation.TableId; +import com.baomidou.mybatisplus.annotation.TableName; +import lombok.*; + +/** + * 网关 DO + * + * @author 内蒙必硕 + */ +@TableName("iot_gateway") +@KeySequence("iot_gateway_seq") // 用于 Oracle、PostgreSQL、Kingbase、DB2、H2 数据库的主键自增。如果是 MySQL 等数据库,可不写。 +@Data +@EqualsAndHashCode(callSuper = true) +@ToString(callSuper = true) +@Builder +@NoArgsConstructor +@AllArgsConstructor +public class GatewayDO extends BaseDO { + + /** + * ID + */ + @TableId + private Long id; + /** + * 管理地址 + */ + private String adminIp; + /** + * 管理账号 + */ + private String username; + /** + * 管理密码 + */ + private String password; + /** + * 网关名字 + */ + private String gatewayName; + /** + * 备注 + */ + private String remark; + /** + * 是否启用 + */ + private Boolean isEnable; + /** + * 网关编码 + */ + private String gatewayCode; + /** + * 网关状态 + *

+ * 枚举 {@link TODO iot_gateway_status 对应的类} + */ + private String gatewayStatus; + /** + * 订阅主题 + */ + private String topic; + +} \ No newline at end of file diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/dataobject/orgnode/OrgNodeDO.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/dataobject/orgnode/OrgNodeDO.java new file mode 100644 index 0000000..d9602b4 --- /dev/null +++ b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/dataobject/orgnode/OrgNodeDO.java @@ -0,0 +1,52 @@ +package cn.iocoder.yudao.module.iot.dal.dataobject.orgnode; + +import lombok.*; +import java.util.*; +import java.time.LocalDateTime; +import java.time.LocalDateTime; +import com.baomidou.mybatisplus.annotation.*; +import cn.iocoder.yudao.framework.mybatis.core.dataobject.BaseDO; + +/** + * 客户组织节点表,用于管理客户、车间、产线等多级组织结构 DO + * + * @author 管理员 + */ +@TableName("iot_org_node") +@KeySequence("iot_org_node_seq") // 用于 Oracle、PostgreSQL、Kingbase、DB2、H2 数据库的主键自增。如果是 MySQL 等数据库,可不写。 +@Data +@EqualsAndHashCode(callSuper = true) +@ToString(callSuper = true) +@Builder +@NoArgsConstructor +@AllArgsConstructor +public class OrgNodeDO extends BaseDO { + + /** + * 主键ID,自增长 + */ + @TableId + private Long id; + /** + * 客户ID + */ + private Long customerId; + /** + * 父节点ID,0表示根节点 + */ + private Long parentId; + /** + * 节点类型:1客户 2车间 3产线 + */ + private Integer nodeType; + /** + * 节点名称 + */ + private String name; + /** + * 排序字段,数值越小越靠前 + */ + private Integer sort; + + +} \ No newline at end of file diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/dataobject/ota/IotOtaFirmwareDO.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/dataobject/ota/IotOtaFirmwareDO.java deleted file mode 100644 index 1e26727..0000000 --- a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/dataobject/ota/IotOtaFirmwareDO.java +++ /dev/null @@ -1,72 +0,0 @@ -package cn.iocoder.yudao.module.iot.dal.dataobject.ota; - -import cn.hutool.crypto.digest.DigestAlgorithm; -import cn.iocoder.yudao.framework.mybatis.core.dataobject.BaseDO; -import com.baomidou.mybatisplus.annotation.KeySequence; -import com.baomidou.mybatisplus.annotation.TableId; -import com.baomidou.mybatisplus.annotation.TableName; -import lombok.AllArgsConstructor; -import lombok.Builder; -import lombok.Data; -import lombok.NoArgsConstructor; - -/** - * IoT OTA 固件 DO - * - * @see https://help.aliyun.com/zh/iot/user-guide/ota-upgrade-overview - * - * @author 芋道源码 - */ -@TableName(value = "iot_ota_firmware", autoResultMap = true) -@KeySequence("iot_ota_firmware_seq") // 用于 Oracle、PostgreSQL、Kingbase、DB2、H2 数据库的主键自增。如果是 MySQL 等数据库,可不写。 -@Data -@Builder -@NoArgsConstructor -@AllArgsConstructor -public class IotOtaFirmwareDO extends BaseDO { - - /** - * 固件编号 - */ - @TableId - private Long id; - /** - * 固件名称 - */ - private String name; - /** - * 固件描述 - */ - private String description; - /** - * 版本号 - */ - private String version; - - /** - * 产品编号 - * - * 关联 {@link cn.iocoder.yudao.module.iot.dal.dataobject.product.IotProductDO#getId()} - */ - private Long productId; - - /** - * 固件文件 URL - */ - private String fileUrl; - /** - * 固件文件大小 - */ - private Long fileSize; - /** - * 固件文件签名算法 - * - * 枚举 {@link DigestAlgorithm},目前只使用 MD5 - */ - private String fileDigestAlgorithm; - /** - * 固件文件签名结果 - */ - private String fileDigestValue; - -} \ No newline at end of file diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/dataobject/ota/IotOtaTaskDO.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/dataobject/ota/IotOtaTaskDO.java deleted file mode 100644 index 4c9124b..0000000 --- a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/dataobject/ota/IotOtaTaskDO.java +++ /dev/null @@ -1,70 +0,0 @@ -package cn.iocoder.yudao.module.iot.dal.dataobject.ota; - -import cn.iocoder.yudao.framework.mybatis.core.dataobject.BaseDO; -import cn.iocoder.yudao.module.iot.enums.ota.IotOtaTaskDeviceScopeEnum; -import cn.iocoder.yudao.module.iot.enums.ota.IotOtaTaskStatusEnum; -import com.baomidou.mybatisplus.annotation.KeySequence; -import com.baomidou.mybatisplus.annotation.TableId; -import com.baomidou.mybatisplus.annotation.TableName; -import lombok.AllArgsConstructor; -import lombok.Builder; -import lombok.Data; -import lombok.NoArgsConstructor; - -/** - * IoT OTA 升级任务 DO - * - * @author 芋道源码 - */ -@TableName(value = "iot_ota_task", autoResultMap = true) -@KeySequence("iot_ota_task_seq") // 用于 Oracle、PostgreSQL、Kingbase、DB2、H2 数据库的主键自增。如果是 MySQL 等数据库,可不写。 -@Data -@Builder -@NoArgsConstructor -@AllArgsConstructor -public class IotOtaTaskDO extends BaseDO { - - /** - * 任务编号 - */ - @TableId - private Long id; - /** - * 任务名称 - */ - private String name; - /** - * 任务描述 - */ - private String description; - - /** - * 固件编号 - *

- * 关联 {@link IotOtaFirmwareDO#getId()} - */ - private Long firmwareId; - - /** - * 任务状态 - *

- * 关联 {@link IotOtaTaskStatusEnum} - */ - private Integer status; - - /** - * 设备升级范围 - *

- * 关联 {@link IotOtaTaskDeviceScopeEnum} - */ - private Integer deviceScope; - /** - * 设备总数数量 - */ - private Integer deviceTotalCount; - /** - * 设备成功数量 - */ - private Integer deviceSuccessCount; - -} diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/dataobject/ota/IotOtaTaskRecordDO.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/dataobject/ota/IotOtaTaskRecordDO.java deleted file mode 100644 index d99a1bb..0000000 --- a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/dataobject/ota/IotOtaTaskRecordDO.java +++ /dev/null @@ -1,82 +0,0 @@ -package cn.iocoder.yudao.module.iot.dal.dataobject.ota; - -import cn.iocoder.yudao.framework.mybatis.core.dataobject.BaseDO; -import cn.iocoder.yudao.module.iot.dal.dataobject.device.IotDeviceDO; -import cn.iocoder.yudao.module.iot.dal.dataobject.device.IotDeviceMessageDO; -import cn.iocoder.yudao.module.iot.enums.ota.IotOtaTaskRecordStatusEnum; -import com.baomidou.mybatisplus.annotation.KeySequence; -import com.baomidou.mybatisplus.annotation.TableId; -import com.baomidou.mybatisplus.annotation.TableName; -import lombok.AllArgsConstructor; -import lombok.Builder; -import lombok.Data; -import lombok.NoArgsConstructor; - -/** - * IoT OTA 升级任务记录 DO - * - * @author 芋道源码 - */ -@TableName(value = "iot_ota_task_record", autoResultMap = true) -@KeySequence("iot_ota_task_record_seq") // 用于 Oracle、PostgreSQL、Kingbase、DB2、H2 数据库的主键自增。如果是 MySQL 等数据库,可不写。 -@Data -@Builder -@NoArgsConstructor -@AllArgsConstructor -public class IotOtaTaskRecordDO extends BaseDO { - - public static final String DESCRIPTION_CANCEL_BY_TASK = "管理员手动取消升级任务(批量)"; - - public static final String DESCRIPTION_CANCEL_BY_RECORD = "管理员手动取消升级记录(单个)"; - - /** - * 升级记录编号 - */ - @TableId - private Long id; - - /** - * 固件编号 - * - * 关联 {@link IotOtaFirmwareDO#getId()} - */ - private Long firmwareId; - /** - * 任务编号 - * - * 关联 {@link IotOtaTaskDO#getId()} - */ - private Long taskId; - - /** - * 设备编号 - * - * 关联 {@link IotDeviceDO#getId()} - */ - private Long deviceId; - /** - * 来源的固件编号 - * - * 关联 {@link IotDeviceDO#getFirmwareId()} - */ - private Long fromFirmwareId; - - /** - * 升级状态 - * - * 关联 {@link IotOtaTaskRecordStatusEnum} - */ - private Integer status; - /** - * 升级进度,百分比 - */ - private Integer progress; - /** - * 升级进度描述 - * - * 注意,只记录设备最后一次的升级进度描述 - * 如果想看历史记录,可以查看 {@link IotDeviceMessageDO} 设备日志 - */ - private String description; - -} \ No newline at end of file diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/dataobject/device/IotDeviceGroupDO.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/dataobject/product/ErpProductUnitDO.java similarity index 51% rename from yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/dataobject/device/IotDeviceGroupDO.java rename to yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/dataobject/product/ErpProductUnitDO.java index 7865a44..cc551ad 100644 --- a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/dataobject/device/IotDeviceGroupDO.java +++ b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/dataobject/product/ErpProductUnitDO.java @@ -1,4 +1,4 @@ -package cn.iocoder.yudao.module.iot.dal.dataobject.device; +package cn.iocoder.yudao.module.iot.dal.dataobject.product; import cn.iocoder.yudao.framework.mybatis.core.dataobject.BaseDO; import com.baomidou.mybatisplus.annotation.KeySequence; @@ -7,36 +7,44 @@ import com.baomidou.mybatisplus.annotation.TableName; import lombok.*; /** - * IoT 设备分组 DO + * ERP 产品单位 DO * * @author 芋道源码 */ -@TableName("iot_device_group") -@KeySequence("iot_device_group_seq") // 用于 Oracle、PostgreSQL、Kingbase、DB2、H2 数据库的主键自增。如果是 MySQL 等数据库,可不写。 +@TableName("erp_product_unit") +@KeySequence("erp_product_unit_seq") // 用于 Oracle、PostgreSQL、Kingbase、DB2、H2 数据库的主键自增。如果是 MySQL 等数据库,可不写。 @Data +@EqualsAndHashCode(callSuper = true) +@ToString(callSuper = true) @Builder @NoArgsConstructor @AllArgsConstructor -public class IotDeviceGroupDO extends BaseDO { +public class ErpProductUnitDO extends BaseDO { /** - * 分组 ID + * 单位编号 */ @TableId private Long id; /** - * 分组名字 + * 单位名字 */ private String name; /** - * 分组状态 - * - * 枚举 {@link cn.iocoder.yudao.framework.common.enums.CommonStatusEnum} + * 是否主单位 */ - private Integer status; + private String primaryFlag; + /** + * 关联主单位id + */ + private Long primaryId; /** - * 分组描述 + * 换算比例 */ - private String description; + private Double changeRate; + /** + * 单位状态 + */ + private Integer status; } \ No newline at end of file diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/dataobject/product/IotProductCategoryDO.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/dataobject/product/IotProductCategoryDO.java deleted file mode 100644 index 174342a..0000000 --- a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/dataobject/product/IotProductCategoryDO.java +++ /dev/null @@ -1,46 +0,0 @@ -package cn.iocoder.yudao.module.iot.dal.dataobject.product; - -import cn.iocoder.yudao.framework.mybatis.core.dataobject.BaseDO; -import com.baomidou.mybatisplus.annotation.KeySequence; -import com.baomidou.mybatisplus.annotation.TableId; -import com.baomidou.mybatisplus.annotation.TableName; -import lombok.*; - -/** - * IoT 产品分类 DO - * - * @author 芋道源码 - */ -@TableName("iot_product_category") -@KeySequence("iot_product_category_seq") // 用于 Oracle、PostgreSQL、Kingbase、DB2、H2 数据库的主键自增。如果是 MySQL 等数据库,可不写。 -@Data -@Builder -@NoArgsConstructor -@AllArgsConstructor -public class IotProductCategoryDO extends BaseDO { - - /** - * 分类 ID - */ - @TableId - private Long id; - /** - * 分类名字 - */ - private String name; - /** - * 分类排序 - */ - private Integer sort; - /** - * 分类状态 - * - * 枚举 {@link cn.iocoder.yudao.framework.common.enums.CommonStatusEnum} - */ - private Integer status; - /** - * 分类描述 - */ - private String description; - -} \ No newline at end of file diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/dataobject/product/IotProductDO.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/dataobject/product/IotProductDO.java deleted file mode 100644 index a1a77fc..0000000 --- a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/dataobject/product/IotProductDO.java +++ /dev/null @@ -1,93 +0,0 @@ -package cn.iocoder.yudao.module.iot.dal.dataobject.product; - -import cn.iocoder.yudao.framework.tenant.core.db.TenantBaseDO; -import com.baomidou.mybatisplus.annotation.KeySequence; -import com.baomidou.mybatisplus.annotation.TableId; -import com.baomidou.mybatisplus.annotation.TableName; -import lombok.*; - -/** - * IoT 产品 DO - * - * @author ahh - */ -@TableName("iot_product") -@KeySequence("iot_product_seq") // 用于 Oracle、PostgreSQL、Kingbase、DB2、H2 数据库的主键自增。如果是 MySQL 等数据库,可不写。 -@Data -@Builder -@NoArgsConstructor -@AllArgsConstructor -public class IotProductDO extends TenantBaseDO { - - /** - * 产品 ID - */ - @TableId - private Long id; - /** - * 产品名称 - */ - private String name; - /** - * 产品标识 - */ - private String productKey; - /** - * 产品密钥,用于一型一密动态注册 - */ - private String productSecret; - /** - * 是否开启动态注册 - */ - private Boolean registerEnabled; - /** - * 产品分类编号 - *

- * 关联 {@link IotProductCategoryDO#getId()} - */ - private Long categoryId; - /** - * 产品图标 - */ - private String icon; - /** - * 产品图片 - */ - private String picUrl; - /** - * 产品描述 - */ - private String description; - - /** - * 产品状态 - *

- * 枚举 {@link cn.iocoder.yudao.module.iot.enums.product.IotProductStatusEnum} - */ - private Integer status; - /** - * 设备类型 - *

- * 枚举 {@link cn.iocoder.yudao.module.iot.enums.product.IotProductDeviceTypeEnum} - */ - private Integer deviceType; - /** - * 联网方式 - *

- * 枚举 {@link cn.iocoder.yudao.module.iot.enums.product.IotNetTypeEnum} - */ - private Integer netType; - /** - * 协议类型 - *

- * 枚举 {@link cn.iocoder.yudao.module.iot.core.enums.IotProtocolTypeEnum} - */ - private String protocolType; - /** - * 序列化类型 - *

- * 枚举 {@link cn.iocoder.yudao.module.iot.core.enums.IotSerializeTypeEnum} - */ - private String serializeType; - -} \ No newline at end of file diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/dataobject/rule/IotDataRuleDO.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/dataobject/rule/IotDataRuleDO.java deleted file mode 100644 index 6d67923..0000000 --- a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/dataobject/rule/IotDataRuleDO.java +++ /dev/null @@ -1,109 +0,0 @@ -package cn.iocoder.yudao.module.iot.dal.dataobject.rule; - -import cn.iocoder.yudao.framework.common.enums.CommonStatusEnum; -import cn.iocoder.yudao.framework.mybatis.core.dataobject.BaseDO; -import cn.iocoder.yudao.framework.mybatis.core.type.LongListTypeHandler; -import cn.iocoder.yudao.module.iot.core.enums.IotDeviceMessageMethodEnum; -import cn.iocoder.yudao.module.iot.dal.dataobject.device.IotDeviceDO; -import cn.iocoder.yudao.module.iot.dal.dataobject.product.IotProductDO; -import cn.iocoder.yudao.module.iot.dal.dataobject.thingmodel.IotThingModelDO; -import com.baomidou.mybatisplus.annotation.KeySequence; -import com.baomidou.mybatisplus.annotation.TableField; -import com.baomidou.mybatisplus.annotation.TableName; -import com.baomidou.mybatisplus.extension.handlers.JacksonTypeHandler; -import lombok.AllArgsConstructor; -import lombok.Builder; -import lombok.Data; -import lombok.NoArgsConstructor; - -import javax.validation.constraints.NotEmpty; -import java.util.List; - -/** - * IoT 数据流转规则 DO - * - * 监听 {@link SourceConfig} 数据源,转发到 {@link IotDataSinkDO} 数据目的 - * - * @author 芋道源码 - */ -@TableName(value = "iot_data_rule", autoResultMap = true) -@KeySequence("iot_data_rule_seq") // 用于 Oracle、PostgreSQL、Kingbase、DB2、H2 数据库的主键自增。如果是 MySQL 等数据库,可不写。 -@Data -@Builder -@NoArgsConstructor -@AllArgsConstructor -public class IotDataRuleDO extends BaseDO { - - /** - * 数据流转规格编号 - */ - private Long id; - /** - * 数据流转规格名称 - */ - private String name; - /** - * 数据流转规格描述 - */ - private String description; - /** - * 数据流转规格状态 - * - * 枚举 {@link CommonStatusEnum} - */ - private Integer status; - - /** - * 数据源配置数组 - */ - @TableField(typeHandler = JacksonTypeHandler.class) - private List sourceConfigs; - /** - * 数据目的编号数组 - * - * 关联 {@link IotDataSinkDO#getId()} - */ - @TableField(typeHandler = LongListTypeHandler.class) - private List sinkIds; - - // TODO @芋艿:未来考虑使用 groovy;支持数据处理; - - /** - * 数据源配置 - */ - @Data - public static class SourceConfig { - - /** - * 消息方法 - * - * 枚举 {@link IotDeviceMessageMethodEnum} 中的 upstream 上行部分 - */ - @NotEmpty(message = "消息方法不能为空") - private String method; - - /** - * 产品编号 - * - * 关联 {@link IotProductDO#getId()} - */ - private Long productId; - /** - * 设备编号 - * - * 关联 {@link IotDeviceDO#getId()} - * 特殊:如果为 {@link IotDeviceDO#DEVICE_ID_ALL} 时,则是全部设备 - */ - @NotEmpty(message = "设备编号不能为空") - private Long deviceId; - - /** - * 标识符 - * - * 1. 物模型时,对应:{@link IotThingModelDO#getIdentifier()} - */ - private String identifier; - - } - -} diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/dataobject/rule/IotDataSinkDO.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/dataobject/rule/IotDataSinkDO.java deleted file mode 100644 index a3cb48e..0000000 --- a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/dataobject/rule/IotDataSinkDO.java +++ /dev/null @@ -1,62 +0,0 @@ -package cn.iocoder.yudao.module.iot.dal.dataobject.rule; - -import cn.iocoder.yudao.framework.common.enums.CommonStatusEnum; -import cn.iocoder.yudao.framework.mybatis.core.dataobject.BaseDO; -import cn.iocoder.yudao.module.iot.dal.dataobject.rule.config.IotAbstractDataSinkConfig; -import cn.iocoder.yudao.module.iot.enums.rule.IotDataSinkTypeEnum; -import com.baomidou.mybatisplus.annotation.KeySequence; -import com.baomidou.mybatisplus.annotation.TableField; -import com.baomidou.mybatisplus.annotation.TableId; -import com.baomidou.mybatisplus.annotation.TableName; -import com.baomidou.mybatisplus.extension.handlers.JacksonTypeHandler; -import lombok.AllArgsConstructor; -import lombok.Builder; -import lombok.Data; -import lombok.NoArgsConstructor; - -/** - * IoT 数据流转目的 DO - * - * @author 芋道源码 - */ -@TableName(value = "iot_data_sink", autoResultMap = true) -@KeySequence("iot_data_bridge_seq") // 用于 Oracle、PostgreSQL、Kingbase、DB2、H2 数据库的主键自增。如果是 MySQL 等数据库,可不写。 -@Data -@Builder -@NoArgsConstructor -@AllArgsConstructor -public class IotDataSinkDO extends BaseDO { - - /** - * 数据流转目的编号 - */ - @TableId - private Long id; - /** - * 数据流转目的名称 - */ - private String name; - /** - * 数据流转目的描述 - */ - private String description; - /** - * 数据流转目的状态 - * - * 枚举 {@link CommonStatusEnum} - */ - private Integer status; - - /** - * 数据流转目的类型 - * - * 枚举 {@link IotDataSinkTypeEnum} - */ - private Integer type; - /** - * 数据流转目的配置 - */ - @TableField(typeHandler = JacksonTypeHandler.class) - private IotAbstractDataSinkConfig config; - -} diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/dataobject/rule/IotSceneRuleDO.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/dataobject/rule/IotSceneRuleDO.java deleted file mode 100644 index ecf87db..0000000 --- a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/dataobject/rule/IotSceneRuleDO.java +++ /dev/null @@ -1,251 +0,0 @@ -package cn.iocoder.yudao.module.iot.dal.dataobject.rule; - -import cn.iocoder.yudao.framework.common.enums.CommonStatusEnum; -import cn.iocoder.yudao.framework.tenant.core.db.TenantBaseDO; -import cn.iocoder.yudao.module.iot.core.mq.message.IotDeviceMessage; -import cn.iocoder.yudao.module.iot.dal.dataobject.alert.IotAlertConfigDO; -import cn.iocoder.yudao.module.iot.dal.dataobject.device.IotDeviceDO; -import cn.iocoder.yudao.module.iot.dal.dataobject.product.IotProductDO; -import cn.iocoder.yudao.module.iot.dal.dataobject.thingmodel.IotThingModelDO; -import cn.iocoder.yudao.module.iot.enums.rule.IotSceneRuleActionTypeEnum; -import cn.iocoder.yudao.module.iot.enums.rule.IotSceneRuleConditionOperatorEnum; -import cn.iocoder.yudao.module.iot.enums.rule.IotSceneRuleConditionTypeEnum; -import cn.iocoder.yudao.module.iot.enums.rule.IotSceneRuleTriggerTypeEnum; -import com.baomidou.mybatisplus.annotation.KeySequence; -import com.baomidou.mybatisplus.annotation.TableField; -import com.baomidou.mybatisplus.annotation.TableId; -import com.baomidou.mybatisplus.annotation.TableName; -import com.baomidou.mybatisplus.extension.handlers.JacksonTypeHandler; -import lombok.AllArgsConstructor; -import lombok.Builder; -import lombok.Data; -import lombok.NoArgsConstructor; - -import java.time.LocalDateTime; -import java.util.List; - -/** - * IoT 场景联动规则 DO - * - * @author 芋道源码 - */ -@TableName(value = "iot_scene_rule", autoResultMap = true) -@KeySequence("iot_scene_rule_seq") // 用于 Oracle、PostgreSQL、Kingbase、DB2、H2 数据库的主键自增。如果是 MySQL 等数据库,可不写。 -@Data -@Builder -@NoArgsConstructor -@AllArgsConstructor -public class IotSceneRuleDO extends TenantBaseDO { - - /** - * 场景联动编号 - */ - @TableId - private Long id; - /** - * 场景联动名称 - */ - private String name; - /** - * 场景联动描述 - */ - private String description; - /** - * 场景联动状态 - * - * 枚举 {@link CommonStatusEnum} - */ - private Integer status; - - /** - * 最后触发时间 - */ - private LocalDateTime lastTriggerTime; - - /** - * 场景定义配置 - */ - @TableField(typeHandler = JacksonTypeHandler.class) - private List triggers; - - /** - * 场景动作配置 - */ - @TableField(typeHandler = JacksonTypeHandler.class) - private List actions; - - /** - * 场景定义配置 - */ - @Data - public static class Trigger { - - // ========== 事件部分 ========== - - /** - * 场景事件类型 - * - * 枚举 {@link IotSceneRuleTriggerTypeEnum} - * 1. {@link IotSceneRuleTriggerTypeEnum#DEVICE_STATE_UPDATE} 时,operator 非空,并且 value 为在线状态 - * 2. {@link IotSceneRuleTriggerTypeEnum#DEVICE_PROPERTY_POST} - * {@link IotSceneRuleTriggerTypeEnum#DEVICE_EVENT_POST} 时,identifier、operator 非空,并且 value 为属性值 - * 3. {@link IotSceneRuleTriggerTypeEnum#DEVICE_EVENT_POST} - * {@link IotSceneRuleTriggerTypeEnum#DEVICE_SERVICE_INVOKE} 时,identifier 非空,但是 operator、value 为空 - * 4. {@link IotSceneRuleTriggerTypeEnum#TIMER} 时,conditions 非空,并且设备无关(无需 productId、deviceId 字段) - */ - private Integer type; - - /** - * 产品编号 - * - * 关联 {@link IotProductDO#getId()} - */ - private Long productId; - /** - * 设备编号 - * - * 关联 {@link IotDeviceDO#getId()} - * 特殊:如果为 {@link IotDeviceDO#DEVICE_ID_ALL} 时,则是全部设备 - */ - private Long deviceId; - /** - * 物模型标识符 - * - * 对应:{@link IotThingModelDO#getIdentifier()} - */ - private String identifier; - /** - * 操作符 - * - * 枚举 {@link IotSceneRuleConditionOperatorEnum} - */ - private String operator; - /** - * 参数(属性值、在线状态) - *

- * 如果有多个值,则使用 "," 分隔,类似 "1,2,3"。 - * 例如说,{@link IotSceneRuleConditionOperatorEnum#IN}、{@link IotSceneRuleConditionOperatorEnum#BETWEEN} - */ - private String value; - - /** - * CRON 表达式 - */ - private String cronExpression; - - // ========== 条件部分 ========== - - /** - * 触发条件分组(状态条件分组)的数组 - *

- * 第一层 List:分组与分组之间,是“或”的关系 - * 第二层 List:条件与条件之间,是“且”的关系 - */ - private List> conditionGroups; - - } - - /** - * 触发条件(状态条件) - */ - @Data - public static class TriggerCondition { - - /** - * 触发条件类型 - * - * 枚举 {@link IotSceneRuleConditionTypeEnum} - * 1. {@link IotSceneRuleConditionTypeEnum#DEVICE_STATE} 时,operator 非空,并且 value 为在线状态 - * 2. {@link IotSceneRuleConditionTypeEnum#DEVICE_PROPERTY} 时,identifier、operator 非空,并且 value 为属性值 - * 3. {@link IotSceneRuleConditionTypeEnum#CURRENT_TIME} 时,operator 非空(使用 DATE_TIME_ 和 TIME_ 部分),并且 value 非空 - */ - private Integer type; - - /** - * 产品编号 - * - * 关联 {@link IotProductDO#getId()} - */ - private Long productId; - /** - * 设备编号 - * - * 关联 {@link IotDeviceDO#getId()} - */ - private Long deviceId; - /** - * 标识符(属性) - * - * 关联 {@link IotThingModelDO#getIdentifier()} - */ - private String identifier; - /** - * 操作符 - * - * 枚举 {@link IotSceneRuleConditionOperatorEnum} - */ - private String operator; - /** - * 参数 - * - * 如果有多个值,则使用 "," 分隔,类似 "1,2,3"。 - * 例如说,{@link IotSceneRuleConditionOperatorEnum#IN}、{@link IotSceneRuleConditionOperatorEnum#BETWEEN} - */ - private String param; - - } - - /** - * 场景动作配置 - */ - @Data - public static class Action { - - /** - * 执行类型 - * - * 枚举 {@link IotSceneRuleActionTypeEnum} - * 1. {@link IotSceneRuleActionTypeEnum#DEVICE_PROPERTY_SET} 时,params 非空 - * {@link IotSceneRuleActionTypeEnum#DEVICE_SERVICE_INVOKE} 时,params 非空 - * 2. {@link IotSceneRuleActionTypeEnum#ALERT_TRIGGER} 时,alertConfigId 为空,因为是 {@link IotAlertConfigDO} 里面关联它 - * 3. {@link IotSceneRuleActionTypeEnum#ALERT_RECOVER} 时,alertConfigId 非空 - */ - private Integer type; - - /** - * 产品编号 - * - * 关联 {@link IotProductDO#getId()} - */ - private Long productId; - /** - * 设备编号 - * - * 关联 {@link IotDeviceDO#getId()} - */ - private Long deviceId; - - /** - * 标识符(服务) - *

- * 关联 {@link IotThingModelDO#getIdentifier()} - */ - private String identifier; - - /** - * 请求参数 - * - * 一般来说,对应 {@link IotDeviceMessage#getParams()} 请求参数 - */ - private String params; - - /** - * 告警配置编号 - * - * 关联 {@link IotAlertConfigDO#getId()} - */ - private Long alertConfigId; - - } - -} diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/dataobject/rule/config/IotAbstractDataSinkConfig.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/dataobject/rule/config/IotAbstractDataSinkConfig.java deleted file mode 100644 index b42e1c0..0000000 --- a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/dataobject/rule/config/IotAbstractDataSinkConfig.java +++ /dev/null @@ -1,37 +0,0 @@ -package cn.iocoder.yudao.module.iot.dal.dataobject.rule.config; - -import cn.iocoder.yudao.module.iot.enums.rule.IotDataSinkTypeEnum; -import com.fasterxml.jackson.annotation.JsonSubTypes; -import com.fasterxml.jackson.annotation.JsonTypeInfo; -import lombok.Data; - -/** - * IoT IotDataBridgeConfig 抽象类 - * - * 用于表示数据目的配置数据的通用类型,根据具体的 "type" 字段动态映射到对应的子类 - * 提供多态支持,适用于不同类型的数据结构序列化和反序列化场景。 - * - * @author HUIHUI - */ -@Data -@JsonTypeInfo(use = JsonTypeInfo.Id.NAME, property = "type", visible = true) -@JsonSubTypes({ - @JsonSubTypes.Type(value = IotDataSinkHttpConfig.class, name = "1"), - @JsonSubTypes.Type(value = IotDataSinkTcpConfig.class, name = "2"), - @JsonSubTypes.Type(value = IotDataSinkWebSocketConfig.class, name = "3"), - @JsonSubTypes.Type(value = IotDataSinkMqttConfig.class, name = "10"), - @JsonSubTypes.Type(value = IotDataSinkRedisConfig.class, name = "21"), - @JsonSubTypes.Type(value = IotDataSinkRocketMQConfig.class, name = "30"), - @JsonSubTypes.Type(value = IotDataSinkRabbitMQConfig.class, name = "31"), - @JsonSubTypes.Type(value = IotDataSinkKafkaConfig.class, name = "32"), -}) -public abstract class IotAbstractDataSinkConfig { - - /** - * 配置类型 - * - * 枚举 {@link IotDataSinkTypeEnum#getType()} - */ - private String type; - -} diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/dataobject/rule/config/IotDataSinkHttpConfig.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/dataobject/rule/config/IotDataSinkHttpConfig.java deleted file mode 100644 index 1a702b4..0000000 --- a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/dataobject/rule/config/IotDataSinkHttpConfig.java +++ /dev/null @@ -1,36 +0,0 @@ -package cn.iocoder.yudao.module.iot.dal.dataobject.rule.config; - -import lombok.Data; - -import java.util.Map; - -/** - * IoT HTTP 配置 {@link IotAbstractDataSinkConfig} 实现类 - * - * @author HUIHUI - */ -@Data -public class IotDataSinkHttpConfig extends IotAbstractDataSinkConfig { - - /** - * 请求 URL - */ - private String url; - /** - * 请求方法 - */ - private String method; - /** - * 请求头 - */ - private Map headers; - /** - * 请求参数 - */ - private Map query; - /** - * 请求体 - */ - private String body; - -} \ No newline at end of file diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/dataobject/rule/config/IotDataSinkKafkaConfig.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/dataobject/rule/config/IotDataSinkKafkaConfig.java deleted file mode 100644 index 1516918..0000000 --- a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/dataobject/rule/config/IotDataSinkKafkaConfig.java +++ /dev/null @@ -1,35 +0,0 @@ -package cn.iocoder.yudao.module.iot.dal.dataobject.rule.config; - -import lombok.Data; - -/** - * IoT Kafka 配置 {@link IotAbstractDataSinkConfig} 实现类 - * - * @author HUIHUI - */ -@Data -public class IotDataSinkKafkaConfig extends IotAbstractDataSinkConfig { - - /** - * Kafka 服务器地址 - */ - private String bootstrapServers; - /** - * 用户名 - */ - private String username; - /** - * 密码 - */ - private String password; - /** - * 是否启用 SSL - */ - private Boolean ssl; - - /** - * 主题 - */ - private String topic; - -} diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/dataobject/rule/config/IotDataSinkMqttConfig.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/dataobject/rule/config/IotDataSinkMqttConfig.java deleted file mode 100644 index ebc0869..0000000 --- a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/dataobject/rule/config/IotDataSinkMqttConfig.java +++ /dev/null @@ -1,34 +0,0 @@ -package cn.iocoder.yudao.module.iot.dal.dataobject.rule.config; - -import lombok.Data; - -/** - * IoT MQTT 配置 {@link IotAbstractDataSinkConfig} 实现类 - * - * @author HUIHUI - */ -@Data -public class IotDataSinkMqttConfig extends IotAbstractDataSinkConfig { - - /** - * MQTT 服务器地址 - */ - private String url; - /** - * 用户名 - */ - private String username; - /** - * 密码 - */ - private String password; - /** - * 客户端编号 - */ - private String clientId; - /** - * 主题 - */ - private String topic; - -} \ No newline at end of file diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/dataobject/rule/config/IotDataSinkRabbitMQConfig.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/dataobject/rule/config/IotDataSinkRabbitMQConfig.java deleted file mode 100644 index 0e95603..0000000 --- a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/dataobject/rule/config/IotDataSinkRabbitMQConfig.java +++ /dev/null @@ -1,46 +0,0 @@ -package cn.iocoder.yudao.module.iot.dal.dataobject.rule.config; - -import lombok.Data; - -/** - * IoT RabbitMQ 配置 {@link IotAbstractDataSinkConfig} 实现类 - * - * @author HUIHUI - */ -@Data -public class IotDataSinkRabbitMQConfig extends IotAbstractDataSinkConfig { - - /** - * RabbitMQ 服务器地址 - */ - private String host; - /** - * 端口 - */ - private Integer port; - /** - * 虚拟主机 - */ - private String virtualHost; - /** - * 用户名 - */ - private String username; - /** - * 密码 - */ - private String password; - - /** - * 交换机名称 - */ - private String exchange; - /** - * 路由键 - */ - private String routingKey; - /** - * 队列名称 - */ - private String queue; -} \ No newline at end of file diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/dataobject/rule/config/IotDataSinkRedisConfig.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/dataobject/rule/config/IotDataSinkRedisConfig.java deleted file mode 100644 index 07460ac..0000000 --- a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/dataobject/rule/config/IotDataSinkRedisConfig.java +++ /dev/null @@ -1,64 +0,0 @@ -package cn.iocoder.yudao.module.iot.dal.dataobject.rule.config; - -import cn.iocoder.yudao.framework.common.validation.InEnum; -import cn.iocoder.yudao.module.iot.enums.rule.IotRedisDataStructureEnum; -import lombok.Data; - -/** - * IoT Redis 配置 {@link IotAbstractDataSinkConfig} 实现类 - * - * @author HUIHUI - */ -@Data -public class IotDataSinkRedisConfig extends IotAbstractDataSinkConfig { - - /** - * Redis 服务器地址 - */ - private String host; - /** - * 端口 - */ - private Integer port; - /** - * 密码 - */ - private String password; - /** - * 数据库索引 - */ - private Integer database; - - /** - * Redis 数据结构类型 - *

- * 枚举 {@link IotRedisDataStructureEnum} - */ - @InEnum(IotRedisDataStructureEnum.class) - private Integer dataStructure; - - /** - * 主题/键名 - *

- * 对于不同的数据结构: - * - Stream: 流的键名 - * - Hash: Hash 的键名 - * - List: 列表的键名 - * - Set: 集合的键名 - * - ZSet: 有序集合的键名 - * - String: 字符串的键名 - */ - private String topic; - - /** - * Hash 字段名(仅当 dataStructure 为 HASH 时使用) - */ - private String hashField; - - /** - * ZSet 分数字段(仅当 dataStructure 为 ZSET 时使用) - * 指定消息中哪个字段作为分数,如果不指定则使用当前时间戳 - */ - private String scoreField; - -} diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/dataobject/rule/config/IotDataSinkRocketMQConfig.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/dataobject/rule/config/IotDataSinkRocketMQConfig.java deleted file mode 100644 index 65fd3e0..0000000 --- a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/dataobject/rule/config/IotDataSinkRocketMQConfig.java +++ /dev/null @@ -1,39 +0,0 @@ -package cn.iocoder.yudao.module.iot.dal.dataobject.rule.config; - -import lombok.Data; - -/** - * IoT RocketMQ 配置 {@link IotAbstractDataSinkConfig} 实现类 - * - * @author HUIHUI - */ -@Data -public class IotDataSinkRocketMQConfig extends IotAbstractDataSinkConfig { - - /** - * RocketMQ 名称服务器地址 - */ - private String nameServer; - /** - * 访问密钥 - */ - private String accessKey; - /** - * 秘密钥匙 - */ - private String secretKey; - - /** - * 生产者组 - */ - private String group; - /** - * 主题 - */ - private String topic; - /** - * 标签 - */ - private String tags; - -} \ No newline at end of file diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/dataobject/rule/config/IotDataSinkTcpConfig.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/dataobject/rule/config/IotDataSinkTcpConfig.java deleted file mode 100644 index 513a987..0000000 --- a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/dataobject/rule/config/IotDataSinkTcpConfig.java +++ /dev/null @@ -1,92 +0,0 @@ -package cn.iocoder.yudao.module.iot.dal.dataobject.rule.config; - -import lombok.Data; - -/** - * IoT TCP 配置 {@link IotAbstractDataSinkConfig} 实现类 - * - * @author HUIHUI - */ -@Data -public class IotDataSinkTcpConfig extends IotAbstractDataSinkConfig { - - /** - * 默认连接超时时间(毫秒) - */ - public static final int DEFAULT_CONNECT_TIMEOUT_MS = 5000; - /** - * 默认读取超时时间(毫秒) - */ - public static final int DEFAULT_READ_TIMEOUT_MS = 10000; - /** - * 默认是否启用 SSL - */ - public static final boolean DEFAULT_SSL = false; - /** - * 默认数据格式 - */ - public static final String DEFAULT_DATA_FORMAT = "JSON"; - /** - * 默认心跳间隔时间(毫秒) - */ - public static final long DEFAULT_HEARTBEAT_INTERVAL_MS = 30000L; - /** - * 默认重连间隔时间(毫秒) - */ - public static final long DEFAULT_RECONNECT_INTERVAL_MS = 5000L; - /** - * 默认最大重连次数 - */ - public static final int DEFAULT_MAX_RECONNECT_ATTEMPTS = 3; - - /** - * TCP 服务器地址 - */ - private String host; - - /** - * TCP 服务器端口 - */ - private Integer port; - - /** - * 连接超时时间(毫秒) - */ - private Integer connectTimeoutMs = DEFAULT_CONNECT_TIMEOUT_MS; - - /** - * 读取超时时间(毫秒) - */ - private Integer readTimeoutMs = DEFAULT_READ_TIMEOUT_MS; - - /** - * 是否启用 SSL - */ - private Boolean ssl = DEFAULT_SSL; - - /** - * SSL 证书路径(当 ssl=true 时需要) - */ - private String sslCertPath; - - /** - * 数据格式:JSON 或 BINARY - */ - private String dataFormat = DEFAULT_DATA_FORMAT; - - /** - * 心跳间隔时间(毫秒),0 表示不启用心跳 - */ - private Long heartbeatIntervalMs = DEFAULT_HEARTBEAT_INTERVAL_MS; - - /** - * 重连间隔时间(毫秒) - */ - private Long reconnectIntervalMs = DEFAULT_RECONNECT_INTERVAL_MS; - - /** - * 最大重连次数 - */ - private Integer maxReconnectAttempts = DEFAULT_MAX_RECONNECT_ATTEMPTS; - -} \ No newline at end of file diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/dataobject/rule/config/IotDataSinkWebSocketConfig.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/dataobject/rule/config/IotDataSinkWebSocketConfig.java deleted file mode 100644 index 55514da..0000000 --- a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/dataobject/rule/config/IotDataSinkWebSocketConfig.java +++ /dev/null @@ -1,132 +0,0 @@ -package cn.iocoder.yudao.module.iot.dal.dataobject.rule.config; - -import lombok.Data; - -/** - * IoT WebSocket 配置 {@link IotAbstractDataSinkConfig} 实现类 - *

- * 配置设备消息通过 WebSocket 协议发送到外部 WebSocket 服务器 - * 支持 WebSocket (ws://) 和 WebSocket Secure (wss://) 连接 - * - * @author HUIHUI - */ -@Data -public class IotDataSinkWebSocketConfig extends IotAbstractDataSinkConfig { - - /** - * 默认连接超时时间(毫秒) - */ - public static final int DEFAULT_CONNECT_TIMEOUT_MS = 5000; - /** - * 默认发送超时时间(毫秒) - */ - public static final int DEFAULT_SEND_TIMEOUT_MS = 10000; - /** - * 默认心跳间隔时间(毫秒) - */ - public static final long DEFAULT_HEARTBEAT_INTERVAL_MS = 30000L; - /** - * 默认心跳消息内容 - */ - public static final String DEFAULT_HEARTBEAT_MESSAGE = "{\"type\":\"heartbeat\"}"; - /** - * 默认是否启用 SSL 证书验证 - */ - public static final boolean DEFAULT_VERIFY_SSL_CERT = true; - /** - * 默认数据格式 - */ - public static final String DEFAULT_DATA_FORMAT = "JSON"; - /** - * 默认重连间隔时间(毫秒) - */ - public static final long DEFAULT_RECONNECT_INTERVAL_MS = 5000L; - /** - * 默认最大重连次数 - */ - public static final int DEFAULT_MAX_RECONNECT_ATTEMPTS = 3; - /** - * 默认是否启用压缩 - */ - public static final boolean DEFAULT_ENABLE_COMPRESSION = false; - /** - * 默认消息发送重试次数 - */ - public static final int DEFAULT_SEND_RETRY_COUNT = 1; - /** - * 默认消息发送重试间隔(毫秒) - */ - public static final long DEFAULT_SEND_RETRY_INTERVAL_MS = 1000L; - - /** - * WebSocket 服务器地址 - * 例如:ws://localhost:8080/ws 或 wss://example.com/ws - */ - private String serverUrl; - - /** - * 连接超时时间(毫秒) - */ - private Integer connectTimeoutMs = DEFAULT_CONNECT_TIMEOUT_MS; - - /** - * 发送超时时间(毫秒) - */ - private Integer sendTimeoutMs = DEFAULT_SEND_TIMEOUT_MS; - - /** - * 心跳间隔时间(毫秒),0 表示不启用心跳 - */ - private Long heartbeatIntervalMs = DEFAULT_HEARTBEAT_INTERVAL_MS; - - /** - * 心跳消息内容(JSON 格式) - */ - private String heartbeatMessage = DEFAULT_HEARTBEAT_MESSAGE; - - /** - * 子协议列表(逗号分隔) - */ - private String subprotocols; - - /** - * 自定义请求头(JSON 格式) - */ - private String customHeaders; - - /** - * 是否启用 SSL 证书验证(仅对 wss:// 生效) - */ - private Boolean verifySslCert = DEFAULT_VERIFY_SSL_CERT; - - /** - * 数据格式:JSON 或 TEXT - */ - private String dataFormat = DEFAULT_DATA_FORMAT; - - /** - * 重连间隔时间(毫秒) - */ - private Long reconnectIntervalMs = DEFAULT_RECONNECT_INTERVAL_MS; - - /** - * 最大重连次数 - */ - private Integer maxReconnectAttempts = DEFAULT_MAX_RECONNECT_ATTEMPTS; - - /** - * 是否启用压缩 - */ - private Boolean enableCompression = DEFAULT_ENABLE_COMPRESSION; - - /** - * 消息发送重试次数 - */ - private Integer sendRetryCount = DEFAULT_SEND_RETRY_COUNT; - - /** - * 消息发送重试间隔(毫秒) - */ - private Long sendRetryIntervalMs = DEFAULT_SEND_RETRY_INTERVAL_MS; - -} \ No newline at end of file diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/dataobject/thingmodel/IotThingModelDO.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/dataobject/thingmodel/IotThingModelDO.java deleted file mode 100644 index d70d2e1..0000000 --- a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/dataobject/thingmodel/IotThingModelDO.java +++ /dev/null @@ -1,91 +0,0 @@ -package cn.iocoder.yudao.module.iot.dal.dataobject.thingmodel; - -import cn.iocoder.yudao.framework.mybatis.core.dataobject.BaseDO; -import cn.iocoder.yudao.module.iot.dal.dataobject.thingmodel.model.ThingModelEvent; -import cn.iocoder.yudao.module.iot.dal.dataobject.thingmodel.model.ThingModelProperty; -import cn.iocoder.yudao.module.iot.dal.dataobject.thingmodel.model.ThingModelService; -import cn.iocoder.yudao.module.iot.dal.dataobject.product.IotProductDO; -import cn.iocoder.yudao.module.iot.enums.thingmodel.IotThingModelTypeEnum; -import com.baomidou.mybatisplus.annotation.KeySequence; -import com.baomidou.mybatisplus.annotation.TableField; -import com.baomidou.mybatisplus.annotation.TableId; -import com.baomidou.mybatisplus.annotation.TableName; -import com.baomidou.mybatisplus.extension.handlers.JacksonTypeHandler; -import lombok.AllArgsConstructor; -import lombok.Builder; -import lombok.Data; -import lombok.NoArgsConstructor; - -/** - * IoT 产品物模型功能 DO - *

- * 每个 {@link IotProductDO} 和 {@link IotThingModelDO} 是“一对多”的关系,它的每个属性、事件、服务都对应一条记录 - * - * @author 芋道源码 - */ -@TableName(value = "iot_thing_model", autoResultMap = true) -@KeySequence("iot_thing_model_seq") // 用于 Oracle、PostgreSQL、Kingbase、DB2、H2 数据库的主键自增。如果是 MySQL 等数据库,可不写。 -@Data -@Builder -@NoArgsConstructor -@AllArgsConstructor -public class IotThingModelDO extends BaseDO { - - /** - * 物模型功能编号 - */ - @TableId - private Long id; - - /** - * 功能标识 - */ - private String identifier; - /** - * 功能名称 - */ - private String name; - /** - * 功能描述 - */ - private String description; - - /** - * 产品标识 - *

- * 关联 {@link IotProductDO#getId()} - */ - private Long productId; - /** - * 产品标识 - *

- * 关联 {@link IotProductDO#getProductKey()} - */ - private String productKey; - - /** - * 功能类型 - *

- * 枚举 {@link IotThingModelTypeEnum} - */ - private Integer type; - - /** - * 属性 - */ - @TableField(typeHandler = JacksonTypeHandler.class) - private ThingModelProperty property; - - /** - * 事件 - */ - @TableField(typeHandler = JacksonTypeHandler.class) - private ThingModelEvent event; - - /** - * 服务 - */ - @TableField(typeHandler = JacksonTypeHandler.class) - private ThingModelService service; - -} \ No newline at end of file diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/dataobject/thingmodel/model/ThingModelEvent.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/dataobject/thingmodel/model/ThingModelEvent.java deleted file mode 100644 index 8fc6112..0000000 --- a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/dataobject/thingmodel/model/ThingModelEvent.java +++ /dev/null @@ -1,56 +0,0 @@ -package cn.iocoder.yudao.module.iot.dal.dataobject.thingmodel.model; - -import cn.iocoder.yudao.framework.common.validation.InEnum; -import cn.iocoder.yudao.module.iot.enums.thingmodel.IotThingModelServiceEventTypeEnum; -import lombok.Data; - -import javax.validation.Valid; -import javax.validation.constraints.NotEmpty; -import javax.validation.constraints.Pattern; -import java.util.List; - -// TODO @puhui999:感觉这个,是不是放到 dal 里会好点?(讨论下,先不改哈) -/** - * IoT 物模型中的事件 - * - * @author HUIHUI - */ -@Data -public class ThingModelEvent { - - /** - * 事件标识符 - */ - @NotEmpty(message = "事件标识符不能为空") - @Pattern(regexp = "^[a-zA-Z][a-zA-Z0-9_]{0,31}$", message = "事件标识符只能由字母、数字和下划线组成,必须以字母开头,长度不超过 32 个字符") - private String identifier; - /** - * 事件名称 - */ - @NotEmpty(message = "事件名称不能为空") - private String name; - /** - * 是否是标准品类的必选事件 - */ - private Boolean required; - /** - * 事件类型 - * - * 枚举 {@link IotThingModelServiceEventTypeEnum} - */ - @NotEmpty(message = "事件类型不能为空") - @InEnum(IotThingModelServiceEventTypeEnum.class) - private String type; - /** - * 事件的输出参数 - * - * 输出参数定义事件调用后返回的结果或反馈信息,用于确认操作结果或提供额外的信息。 - */ - @Valid - private List outputParams; - /** - * 标识设备需要执行的具体操作 - */ - private String method; - -} diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/dataobject/thingmodel/model/ThingModelParam.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/dataobject/thingmodel/model/ThingModelParam.java deleted file mode 100644 index b3aa1b9..0000000 --- a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/dataobject/thingmodel/model/ThingModelParam.java +++ /dev/null @@ -1,63 +0,0 @@ -package cn.iocoder.yudao.module.iot.dal.dataobject.thingmodel.model; - -import cn.iocoder.yudao.framework.common.validation.InEnum; -import cn.iocoder.yudao.module.iot.dal.dataobject.thingmodel.model.dataType.ThingModelDataSpecs; -import cn.iocoder.yudao.module.iot.enums.thingmodel.IotDataSpecsDataTypeEnum; -import cn.iocoder.yudao.module.iot.enums.thingmodel.IotThingModelParamDirectionEnum; -import lombok.Data; - -import javax.validation.constraints.NotEmpty; -import javax.validation.constraints.Pattern; -import java.util.List; - -/** - * IoT 产品物模型中的参数 - * - * @author HUIHUI - */ -@Data -public class ThingModelParam { - - /** - * 参数标识符 - */ - @NotEmpty(message = "参数标识符不能为空") - @Pattern(regexp = "^[a-zA-Z][a-zA-Z0-9_]{0,31}$", message = "参数标识符只能由字母、数字和下划线组成,必须以字母开头,长度不超过 32 个字符") - private String identifier; - /** - * 参数名称 - */ - @NotEmpty(message = "参数名称不能为空") - private String name; - /** - * 用于区分输入或输出参数 - * - * 枚举 {@link IotThingModelParamDirectionEnum} - */ - @NotEmpty(message = "参数方向不能为空") - @InEnum(IotThingModelParamDirectionEnum.class) - private String direction; - /** - * 参数的序号。从 0 开始排序,且不能重复。 - * - * TODO 考虑要不要序号,感觉是要的, 先留一手看看 - */ - private Integer paraOrder; - /** - * 参数值的数据类型,与 dataSpecs 的 dataType 保持一致 - * - * 枚举 {@link IotDataSpecsDataTypeEnum} - */ - @NotEmpty(message = "数据类型不能为空") - @InEnum(IotDataSpecsDataTypeEnum.class) - private String dataType; - /** - * 参数值的数据类型(dataType)为非列表型(int、float、double、text、date、array)的数据规范存储在 dataSpecs 中 - */ - private ThingModelDataSpecs dataSpecs; - /** - * 参数值的数据类型(dataType)为列表型(enum、bool、struct)的数据规范存储在 dataSpecsList 中 - */ - private List dataSpecsList; - -} diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/dataobject/thingmodel/model/ThingModelProperty.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/dataobject/thingmodel/model/ThingModelProperty.java deleted file mode 100644 index 5a33837..0000000 --- a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/dataobject/thingmodel/model/ThingModelProperty.java +++ /dev/null @@ -1,63 +0,0 @@ -package cn.iocoder.yudao.module.iot.dal.dataobject.thingmodel.model; - -import cn.iocoder.yudao.framework.common.validation.InEnum; -import cn.iocoder.yudao.module.iot.dal.dataobject.thingmodel.model.dataType.ThingModelDataSpecs; -import cn.iocoder.yudao.module.iot.enums.thingmodel.IotDataSpecsDataTypeEnum; -import cn.iocoder.yudao.module.iot.enums.thingmodel.IotThingModelAccessModeEnum; -import lombok.Data; - -import javax.validation.constraints.NotEmpty; -import javax.validation.constraints.Pattern; -import java.util.List; - -/** - * IoT 物模型中的属性 - * - * dataSpecs 和 dataSpecsList 之中必须传入且只能传入一个 - * - * @author HUIHUI - */ -@Data -public class ThingModelProperty { - - /** - * 属性标识符 - */ - @NotEmpty(message = "属性标识符不能为空") - @Pattern(regexp = "^[a-zA-Z][a-zA-Z0-9_]{0,31}$", message = "属性标识符只能由字母、数字和下划线组成,必须以字母开头,长度不超过 32 个字符") - private String identifier; - /** - * 属性名称 - */ - @NotEmpty(message = "属性名称不能为空") - private String name; - /** - * 云端可以对该属性进行的操作类型 - * - * 枚举 {@link IotThingModelAccessModeEnum} - */ - @NotEmpty(message = "操作类型不能为空") - @InEnum(IotThingModelAccessModeEnum.class) - private String accessMode; - /** - * 是否是标准品类的必选服务 - */ - private Boolean required; - /** - * 参数值的数据类型,与 dataSpecs 的 dataType 保持一致 - * - * 枚举 {@link IotDataSpecsDataTypeEnum} - */ - @NotEmpty(message = "数据类型不能为空") - @InEnum(IotDataSpecsDataTypeEnum.class) - private String dataType; - /** - * 数据类型(dataType)为非列表型(int、float、double、text、date、array)的数据规范存储在 dataSpecs 中 - */ - private ThingModelDataSpecs dataSpecs; - /** - * 数据类型(dataType)为列表型(enum、bool、struct)的数据规范存储在 dataSpecsList 中 - */ - private List dataSpecsList; - -} diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/dataobject/thingmodel/model/ThingModelService.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/dataobject/thingmodel/model/ThingModelService.java deleted file mode 100644 index 05abc72..0000000 --- a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/dataobject/thingmodel/model/ThingModelService.java +++ /dev/null @@ -1,62 +0,0 @@ -package cn.iocoder.yudao.module.iot.dal.dataobject.thingmodel.model; - -import cn.iocoder.yudao.framework.common.validation.InEnum; -import cn.iocoder.yudao.module.iot.enums.thingmodel.IotThingModelServiceCallTypeEnum; -import lombok.Data; - -import javax.validation.Valid; -import javax.validation.constraints.NotEmpty; -import javax.validation.constraints.Pattern; -import java.util.List; - -/** - * IoT 物模型中的服务 - * - * @author HUIHUI - */ -@Data -public class ThingModelService { - - /** - * 服务标识符 - */ - @NotEmpty(message = "服务标识符不能为空") - @Pattern(regexp = "^[a-zA-Z][a-zA-Z0-9_]{0,31}$", message = "服务标识符只能由字母、数字和下划线组成,必须以字母开头,长度不超过 32 个字符") - private String identifier; - /** - * 服务名称 - */ - @NotEmpty(message = "服务名称不能为空") - private String name; - /** - * 是否是标准品类的必选服务 - */ - private Boolean required; - /** - * 调用类型 - * - * 枚举 {@link IotThingModelServiceCallTypeEnum} - */ - @NotEmpty(message = "调用类型不能为空") - @InEnum(IotThingModelServiceCallTypeEnum.class) - private String callType; - /** - * 服务的输入参数 - * - * 输入参数定义服务调用时所需提供的信息,用于控制设备行为或执行特定任务 - */ - @Valid - private List inputParams; - /** - * 服务的输出参数 - * - * 输出参数定义服务调用后返回的结果或反馈信息,用于确认操作结果或提供额外的信息。 - */ - @Valid - private List outputParams; - /** - * 标识设备需要执行的具体操作 - */ - private String method; - -} diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/dataobject/thingmodel/model/dataType/ThingModelArrayDataSpecs.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/dataobject/thingmodel/model/dataType/ThingModelArrayDataSpecs.java deleted file mode 100644 index b022399..0000000 --- a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/dataobject/thingmodel/model/dataType/ThingModelArrayDataSpecs.java +++ /dev/null @@ -1,37 +0,0 @@ -package cn.iocoder.yudao.module.iot.dal.dataobject.thingmodel.model.dataType; - -import com.fasterxml.jackson.annotation.JsonIgnoreProperties; -import lombok.Data; -import lombok.EqualsAndHashCode; - -import javax.validation.Valid; -import javax.validation.constraints.NotEmpty; -import javax.validation.constraints.NotNull; -import javax.validation.constraints.Pattern; -import java.util.List; - -/** - * IoT 物模型数据类型为数组的 DataSpec 定义 - * - * @author HUIHUI - */ -@Data -@EqualsAndHashCode(callSuper = true) -@JsonIgnoreProperties({"dataType"}) // 忽略子类中的 dataType 字段,从而避免重复 -public class ThingModelArrayDataSpecs extends ThingModelDataSpecs { - - @NotNull(message = "数组元素个数不能为空") - private Integer size; - - @NotEmpty(message = "数组元素的数据类型不能为空") - @Pattern(regexp = "^(struct|int|float|double|text)$", message = "数组元素的数据类型必须为:struct、int、float、double 或 text") - private String childDataType; - /** - * 数据类型(childDataType)为列表型 struct 的数据规范存储在 dataSpecsList 中 - * 此时 struct 取值范围为:int、float、double、text、date、enum、bool - */ - @Valid - private List dataSpecsList; - -} - diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/dataobject/thingmodel/model/dataType/ThingModelBoolOrEnumDataSpecs.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/dataobject/thingmodel/model/dataType/ThingModelBoolOrEnumDataSpecs.java deleted file mode 100644 index fa8b1ff..0000000 --- a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/dataobject/thingmodel/model/dataType/ThingModelBoolOrEnumDataSpecs.java +++ /dev/null @@ -1,31 +0,0 @@ -package cn.iocoder.yudao.module.iot.dal.dataobject.thingmodel.model.dataType; - -import com.fasterxml.jackson.annotation.JsonIgnoreProperties; -import lombok.Data; -import lombok.EqualsAndHashCode; - -import javax.validation.constraints.NotEmpty; -import javax.validation.constraints.NotNull; -import javax.validation.constraints.Pattern; - -/** - * IoT 物模型数据类型为布尔型或枚举型的 DataSpec 定义 - * - * 数据类型,取值为 bool 或 enum - * - * @author HUIHUI - */ -@Data -@EqualsAndHashCode(callSuper = true) -@JsonIgnoreProperties({"dataType"}) // 忽略子类中的 dataType 字段,从而避免重复 -public class ThingModelBoolOrEnumDataSpecs extends ThingModelDataSpecs { - - @NotEmpty(message = "枚举项的名称不能为空") - @Pattern(regexp = "^[\\u4e00-\\u9fa5a-zA-Z0-9][\\u4e00-\\u9fa5a-zA-Z0-9_-]{0,19}$", - message = "枚举项的名称只能包含中文、大小写英文字母、数字、下划线和短划线,必须以中文、英文字母或数字开头,长度不超过 20 个字符") - private String name; - - @NotNull(message = "枚举值不能为空") - private Integer value; - -} diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/dataobject/thingmodel/model/dataType/ThingModelDataSpecs.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/dataobject/thingmodel/model/dataType/ThingModelDataSpecs.java deleted file mode 100644 index 1643ab2..0000000 --- a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/dataobject/thingmodel/model/dataType/ThingModelDataSpecs.java +++ /dev/null @@ -1,35 +0,0 @@ -package cn.iocoder.yudao.module.iot.dal.dataobject.thingmodel.model.dataType; - -import com.fasterxml.jackson.annotation.JsonSubTypes; -import com.fasterxml.jackson.annotation.JsonTypeInfo; -import lombok.Data; - -/** - * IoT ThingModelDataSpecs 抽象类 - * - * 用于表示物模型数据的通用类型,根据具体的 "dataType" 字段动态映射到对应的子类 - * 提供多态支持,适用于不同类型的数据结构序列化和反序列化场景 - * - * @author HUIHUI - */ -@Data -@JsonTypeInfo(use = JsonTypeInfo.Id.NAME, property = "dataType", visible = true) -@JsonSubTypes({ - @JsonSubTypes.Type(value = ThingModelNumericDataSpec.class, name = "int"), - @JsonSubTypes.Type(value = ThingModelNumericDataSpec.class, name = "float"), - @JsonSubTypes.Type(value = ThingModelNumericDataSpec.class, name = "double"), - @JsonSubTypes.Type(value = ThingModelDateOrTextDataSpecs.class, name = "text"), - @JsonSubTypes.Type(value = ThingModelDateOrTextDataSpecs.class, name = "date"), - @JsonSubTypes.Type(value = ThingModelBoolOrEnumDataSpecs.class, name = "bool"), - @JsonSubTypes.Type(value = ThingModelBoolOrEnumDataSpecs.class, name = "enum"), - @JsonSubTypes.Type(value = ThingModelArrayDataSpecs.class, name = "array"), - @JsonSubTypes.Type(value = ThingModelStructDataSpecs.class, name = "struct") -}) -public abstract class ThingModelDataSpecs { - - /** - * 数据类型 - */ - private String dataType; - -} diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/dataobject/thingmodel/model/dataType/ThingModelDateOrTextDataSpecs.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/dataobject/thingmodel/model/dataType/ThingModelDateOrTextDataSpecs.java deleted file mode 100644 index 31d9559..0000000 --- a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/dataobject/thingmodel/model/dataType/ThingModelDateOrTextDataSpecs.java +++ /dev/null @@ -1,34 +0,0 @@ -package cn.iocoder.yudao.module.iot.dal.dataobject.thingmodel.model.dataType; - -import com.fasterxml.jackson.annotation.JsonIgnoreProperties; -import lombok.Data; -import lombok.EqualsAndHashCode; - -import javax.validation.constraints.Max; - -/** - * IoT 物模型数据类型为时间型或文本型的 DataSpec 定义 - * - * 数据类型,取值为 date 或 text - * - * @author HUIHUI - */ -@Data -@EqualsAndHashCode(callSuper = true) -@JsonIgnoreProperties({"dataType"}) // 忽略子类中的 dataType 字段,从而避免重复 -public class ThingModelDateOrTextDataSpecs extends ThingModelDataSpecs { - - /** - * 数据长度,单位为字节。取值不能超过 2048 - * - * 当 dataType 为 text 时,需传入该参数 - */ - @Max(value = 2048, message = "数据长度不能超过 2048") - private Integer length; - /** - * 默认值,可选参数,用于存储默认值 - */ - private String defaultValue; - -} - diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/dataobject/thingmodel/model/dataType/ThingModelNumericDataSpec.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/dataobject/thingmodel/model/dataType/ThingModelNumericDataSpec.java deleted file mode 100644 index 18bbc41..0000000 --- a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/dataobject/thingmodel/model/dataType/ThingModelNumericDataSpec.java +++ /dev/null @@ -1,57 +0,0 @@ -package cn.iocoder.yudao.module.iot.dal.dataobject.thingmodel.model.dataType; - -import com.fasterxml.jackson.annotation.JsonIgnoreProperties; -import lombok.Data; -import lombok.EqualsAndHashCode; - -import javax.validation.constraints.NotEmpty; -import javax.validation.constraints.Pattern; - -/** - * IoT 物模型数据类型为数值的 DataSpec 定义 - * - * 数据类型,取值为 int、float 或 double - * - * @author HUIHUI - */ -@Data -@EqualsAndHashCode(callSuper = true) -@JsonIgnoreProperties({"dataType"}) // 忽略子类中的 dataType 字段,从而避免重复 -public class ThingModelNumericDataSpec extends ThingModelDataSpecs { - - /** - * 最大值,需转为字符串类型。值必须与 dataType 类型一致 - */ - @NotEmpty(message = "最大值不能为空") - @Pattern(regexp = "^-?\\d+(\\.\\d+)?$", message = "最大值必须为数值类型") - private String max; - /** - * 最小值,需转为字符串类型。值必须与 dataType 类型一致 - */ - @NotEmpty(message = "最小值不能为空") - @Pattern(regexp = "^-?\\d+(\\.\\d+)?$", message = "最小值必须为数值类型") - private String min; - /** - * 步长,需转为字符串类型。值必须与 dataType 类型一致 - */ - @NotEmpty(message = "步长不能为空") - @Pattern(regexp = "^-?\\d+(\\.\\d+)?$", message = "步长必须为数值类型") - private String step; - /** - * 精度。当 dataType 为 float 或 double 时可选传入 - */ - private String precise; - /** - * 默认值,可传入用于存储的默认值 - */ - private String defaultValue; - /** - * 单位的符号 - */ - private String unit; - /** - * 单位的名称 - */ - private String unitName; - -} diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/dataobject/thingmodel/model/dataType/ThingModelStructDataSpecs.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/dataobject/thingmodel/model/dataType/ThingModelStructDataSpecs.java deleted file mode 100644 index d4a0860..0000000 --- a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/dataobject/thingmodel/model/dataType/ThingModelStructDataSpecs.java +++ /dev/null @@ -1,57 +0,0 @@ -package cn.iocoder.yudao.module.iot.dal.dataobject.thingmodel.model.dataType; - -import cn.iocoder.yudao.framework.common.validation.InEnum; -import cn.iocoder.yudao.module.iot.enums.thingmodel.IotThingModelAccessModeEnum; -import com.fasterxml.jackson.annotation.JsonIgnoreProperties; -import lombok.Data; -import lombok.EqualsAndHashCode; - -import javax.validation.Valid; -import javax.validation.constraints.NotEmpty; -import javax.validation.constraints.Pattern; -import java.util.List; - -/** - * IoT 物模型数据类型为 struct 的 DataSpec 定义 - * - * @author HUIHUI - */ -@Data -@EqualsAndHashCode(callSuper = true) -@JsonIgnoreProperties({"dataType"}) // 忽略子类中的 dataType 字段,从而避免重复 -public class ThingModelStructDataSpecs extends ThingModelDataSpecs { - - @NotEmpty(message = "属性标识符不能为空") - @Pattern(regexp = "^[a-zA-Z][a-zA-Z0-9_]{0,31}$", message = "属性标识符只能由字母、数字和下划线组成,必须以字母开头,长度不超过 32 个字符") - private String identifier; - - @NotEmpty(message = "属性名称不能为空") - private String name; - - @NotEmpty(message = "操作类型不能为空") - @InEnum(IotThingModelAccessModeEnum.class) - private String accessMode; - - /** - * 是否是标准品类的必选服务 - */ - private Boolean required; - - @NotEmpty(message = "数据类型不能为空") - @Pattern(regexp = "^(int|float|double|text|date|enum|bool)$", message = "数据类型必须为:int、float、double、text、date、enum、bool") - private String childDataType; - - /** - * 数据类型(dataType)为非列表型(int、float、double、text、date、array)的数据规范存储在 dataSpecs 中 - */ - @Valid - private ThingModelDataSpecs dataSpecs; - - /** - * 数据类型(dataType)为列表型(enum、bool、struct)的数据规范存储在 dataSpecsList 中 - */ - @Valid - private List dataSpecsList; - -} - diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/mysql/alert/IotAlertConfigMapper.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/mysql/alert/IotAlertConfigMapper.java deleted file mode 100644 index 52b0d36..0000000 --- a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/mysql/alert/IotAlertConfigMapper.java +++ /dev/null @@ -1,39 +0,0 @@ -package cn.iocoder.yudao.module.iot.dal.mysql.alert; - -import cn.iocoder.yudao.framework.common.pojo.PageResult; -import cn.iocoder.yudao.framework.mybatis.core.mapper.BaseMapperX; -import cn.iocoder.yudao.framework.mybatis.core.query.LambdaQueryWrapperX; -import cn.iocoder.yudao.framework.mybatis.core.util.MyBatisUtils; -import cn.iocoder.yudao.module.iot.controller.admin.alert.vo.config.IotAlertConfigPageReqVO; -import cn.iocoder.yudao.module.iot.dal.dataobject.alert.IotAlertConfigDO; -import org.apache.ibatis.annotations.Mapper; - -import java.util.List; - -/** - * IoT 告警配置 Mapper - * - * @author 芋道源码 - */ -@Mapper -public interface IotAlertConfigMapper extends BaseMapperX { - - default PageResult selectPage(IotAlertConfigPageReqVO reqVO) { - return selectPage(reqVO, new LambdaQueryWrapperX() - .likeIfPresent(IotAlertConfigDO::getName, reqVO.getName()) - .eqIfPresent(IotAlertConfigDO::getStatus, reqVO.getStatus()) - .betweenIfPresent(IotAlertConfigDO::getCreateTime, reqVO.getCreateTime()) - .orderByDesc(IotAlertConfigDO::getId)); - } - - default List selectListByStatus(Integer status) { - return selectList(IotAlertConfigDO::getStatus, status); - } - - default List selectListBySceneRuleIdAndStatus(Long sceneRuleId, Integer status) { - return selectList(new LambdaQueryWrapperX() - .eq(IotAlertConfigDO::getStatus, status) - .apply(MyBatisUtils.findInSet("scene_rule_ids", sceneRuleId))); - } - -} \ No newline at end of file diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/mysql/alert/IotAlertRecordMapper.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/mysql/alert/IotAlertRecordMapper.java deleted file mode 100644 index f23fe60..0000000 --- a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/mysql/alert/IotAlertRecordMapper.java +++ /dev/null @@ -1,46 +0,0 @@ -package cn.iocoder.yudao.module.iot.dal.mysql.alert; - -import cn.iocoder.yudao.framework.common.pojo.PageResult; -import cn.iocoder.yudao.framework.mybatis.core.mapper.BaseMapperX; -import cn.iocoder.yudao.framework.mybatis.core.query.LambdaQueryWrapperX; -import cn.iocoder.yudao.module.iot.controller.admin.alert.vo.recrod.IotAlertRecordPageReqVO; -import cn.iocoder.yudao.module.iot.dal.dataobject.alert.IotAlertRecordDO; -import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper; -import org.apache.ibatis.annotations.Mapper; - -import java.util.Collection; -import java.util.List; - -/** - * IoT 告警记录 Mapper - * - * @author 芋道源码 - */ -@Mapper -public interface IotAlertRecordMapper extends BaseMapperX { - - default PageResult selectPage(IotAlertRecordPageReqVO reqVO) { - return selectPage(reqVO, new LambdaQueryWrapperX() - .eqIfPresent(IotAlertRecordDO::getConfigId, reqVO.getConfigId()) - .eqIfPresent(IotAlertRecordDO::getConfigLevel, reqVO.getLevel()) - .eqIfPresent(IotAlertRecordDO::getProductId, reqVO.getProductId()) - .eqIfPresent(IotAlertRecordDO::getDeviceId, reqVO.getDeviceId()) - .eqIfPresent(IotAlertRecordDO::getProcessStatus, reqVO.getProcessStatus()) - .betweenIfPresent(IotAlertRecordDO::getCreateTime, reqVO.getCreateTime()) - .orderByDesc(IotAlertRecordDO::getId)); - } - - default List selectListBySceneRuleId(Long sceneRuleId, Long deviceId, Boolean processStatus) { - return selectList(new LambdaQueryWrapperX() - .eq(IotAlertRecordDO::getSceneRuleId, sceneRuleId) - .eqIfPresent(IotAlertRecordDO::getDeviceId, deviceId) - .eqIfPresent(IotAlertRecordDO::getProcessStatus, processStatus) - .orderByDesc(IotAlertRecordDO::getId)); - } - - default int updateList(Collection ids, IotAlertRecordDO updateObj) { - return update(updateObj, new LambdaUpdateWrapper() - .in(IotAlertRecordDO::getId, ids)); - } - -} \ No newline at end of file diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/mysql/device/DeviceAttributeMapper.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/mysql/device/DeviceAttributeMapper.java new file mode 100644 index 0000000..7e02645 --- /dev/null +++ b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/mysql/device/DeviceAttributeMapper.java @@ -0,0 +1,50 @@ +package cn.iocoder.yudao.module.iot.dal.mysql.device; + +import cn.iocoder.yudao.framework.common.pojo.PageParam; +import cn.iocoder.yudao.framework.common.pojo.PageResult; +import cn.iocoder.yudao.framework.mybatis.core.mapper.BaseMapperX; +import cn.iocoder.yudao.framework.mybatis.core.query.LambdaQueryWrapperX; +import cn.iocoder.yudao.module.iot.dal.dataobject.device.DeviceAttributeDO; +import org.apache.ibatis.annotations.Mapper; + +import java.util.List; + +/** + * 设备属性 Mapper + * + * @author 内蒙必硕 + */ +@Mapper +public interface DeviceAttributeMapper extends BaseMapperX { + + default PageResult selectPage(PageParam reqVO, Long deviceId) { + return selectPage(reqVO, new LambdaQueryWrapperX() + .eq(DeviceAttributeDO::getDeviceId, deviceId) + .orderByDesc(DeviceAttributeDO::getId)); + } + + default int deleteByDeviceId(Long deviceId) { + return delete(DeviceAttributeDO::getDeviceId, deviceId); + } + + default List selectList(DeviceAttributeDO reqVO) { + return selectList(new LambdaQueryWrapperX() + .eqIfPresent(DeviceAttributeDO::getDeviceId, reqVO.getDeviceId()) + .likeIfPresent(DeviceAttributeDO::getAttributeName, reqVO.getAttributeName()) + .eqIfPresent(DeviceAttributeDO::getAlertId, reqVO.getAlertId()) + .eqIfPresent(DeviceAttributeDO::getGatewayId, reqVO.getGatewayId()) + .eqIfPresent(DeviceAttributeDO::getIsEnable, reqVO.getIsEnable()) + .eqIfPresent(DeviceAttributeDO::getIoType, reqVO.getIoType()) + .orderByDesc(DeviceAttributeDO::getId)); + } + default List selectAlertList(DeviceAttributeDO reqVO) { + return selectList(new LambdaQueryWrapperX() + .eqIfPresent(DeviceAttributeDO::getDeviceId, reqVO.getDeviceId()) + .likeIfPresent(DeviceAttributeDO::getAttributeName, reqVO.getAttributeName()) + .eqIfPresent(DeviceAttributeDO::getGatewayId, reqVO.getGatewayId()) + .eqIfPresent(DeviceAttributeDO::getIsEnable, reqVO.getIsEnable()) + .eqIfPresent(DeviceAttributeDO::getIoType, reqVO.getIoType()) + .gtIfPresent(DeviceAttributeDO::getAlertId, 0) + .orderByDesc(DeviceAttributeDO::getId)); + } +} \ No newline at end of file diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/mysql/device/DeviceMapper.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/mysql/device/DeviceMapper.java new file mode 100644 index 0000000..f467c9f --- /dev/null +++ b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/mysql/device/DeviceMapper.java @@ -0,0 +1,130 @@ +package cn.iocoder.yudao.module.iot.dal.mysql.device; + +import cn.iocoder.yudao.framework.common.pojo.PageParam; +import cn.iocoder.yudao.framework.common.pojo.PageResult; +import cn.iocoder.yudao.framework.mybatis.core.mapper.BaseMapperX; +import cn.iocoder.yudao.framework.mybatis.core.query.LambdaQueryWrapperX; +import cn.iocoder.yudao.module.iot.controller.admin.device.vo.*; +import cn.iocoder.yudao.module.iot.dal.dataobject.device.DeviceDO; +import com.baomidou.mybatisplus.core.metadata.IPage; +import com.baomidou.mybatisplus.extension.plugins.pagination.Page; +import org.apache.commons.lang3.StringUtils; +import org.apache.ibatis.annotations.Mapper; +import org.apache.ibatis.annotations.Param; +import org.apache.ibatis.annotations.Select; + +import java.util.Arrays; +import java.util.List; +import java.util.Map; +import java.util.stream.Collectors; + +/** + * 物联设备 Mapper + * + * @author 内蒙必硕 + */ +@Mapper +public interface DeviceMapper extends BaseMapperX { + + default PageResult selectPage(DevicePageReqVO reqVO) { + + LambdaQueryWrapperX deviceDOLambdaQueryWrapperX = new LambdaQueryWrapperX<>(); + deviceDOLambdaQueryWrapperX.likeIfPresent(DeviceDO::getDeviceCode, reqVO.getDeviceCode()) + .likeIfPresent(DeviceDO::getDeviceName, reqVO.getDeviceName()) + .likeIfPresent(DeviceDO::getTopic, reqVO.getTopic()) + .eqIfPresent(DeviceDO::getDeviceType, reqVO.getDeviceType()) + .eqIfPresent(DeviceDO::getStatus, reqVO.getStatus()) + .eqIfPresent(DeviceDO::getReadTopic, reqVO.getReadTopic()) + .eqIfPresent(DeviceDO::getWriteTopic, reqVO.getWriteTopic()) + .eqIfPresent(DeviceDO::getGatewayId, reqVO.getGatewayId()) + .eqIfPresent(DeviceDO::getDeviceBrandId, reqVO.getDeviceBrandId()) + .eqIfPresent(DeviceDO::getOffLineDuration, reqVO.getOffLineDuration()) + .betweenIfPresent(DeviceDO::getLastOnlineTime, reqVO.getLastOnlineTime()) + .eqIfPresent(DeviceDO::getRemark, reqVO.getRemark()) + .eqIfPresent(DeviceDO::getIsEnable, reqVO.getIsEnable()) + .betweenIfPresent(DeviceDO::getCreateTime, reqVO.getCreateTime()) + .orderByDesc(DeviceDO::getId); + + // 单独处理 ids 条件 + if (StringUtils.isNotBlank(reqVO.getIds())) { + List idList = Arrays.stream(reqVO.getIds().split(",")) + .map(String::trim) + .map(Long::valueOf) + .collect(Collectors.toList()); + deviceDOLambdaQueryWrapperX.in(DeviceDO::getId, idList); + } + + PageResult deviceDOPageResult = selectPage(reqVO, deviceDOLambdaQueryWrapperX); + + return deviceDOPageResult; + } + + default PageResult selectPage(PageParam reqVO, Long gatewayId) { + return selectPage(reqVO, new LambdaQueryWrapperX() + .eq(DeviceDO::getGatewayId, gatewayId) + .orderByDesc(DeviceDO::getId)); + } + + default int deleteByGatewayId(Long gatewayId) { + return delete(DeviceDO::getGatewayId, gatewayId); + } + + default List selectByGatewayId(Long gatewayId) { + return selectList(DeviceDO::getGatewayId, gatewayId); + } + default DeviceDO selectByName(String name) { + return selectOne(DeviceDO::getDeviceName,name); + } + default DeviceDO selectByTopic(String topic) { + return selectOne(DeviceDO::getReadTopic,topic); + } + default List selectList(DevicePageReqVO reqVO) { + return selectList(new LambdaQueryWrapperX() + .eqIfPresent(DeviceDO::getDeviceCode, reqVO.getDeviceCode()) + .likeIfPresent(DeviceDO::getDeviceName, reqVO.getDeviceName()) + .eqIfPresent(DeviceDO::getDeviceType, reqVO.getDeviceType()) + .eqIfPresent(DeviceDO::getStatus, reqVO.getStatus()) + .eqIfPresent(DeviceDO::getReadTopic, reqVO.getReadTopic()) + .eqIfPresent(DeviceDO::getWriteTopic, reqVO.getWriteTopic()) + .eqIfPresent(DeviceDO::getGatewayId, reqVO.getGatewayId()) + .eqIfPresent(DeviceDO::getDeviceBrandId, reqVO.getDeviceBrandId()) + .eqIfPresent(DeviceDO::getOffLineDuration, reqVO.getOffLineDuration()) + .betweenIfPresent(DeviceDO::getLastOnlineTime, reqVO.getLastOnlineTime()) + .eqIfPresent(DeviceDO::getRemark, reqVO.getRemark()) + .eqIfPresent(DeviceDO::getIsEnable, reqVO.getIsEnable()) + .orderByDesc(DeviceDO::getId)); + } + + IPage lineDevicePage(Page page, @Param("pageReqVO") LineDeviceRequestVO pageReqVO); + + String lineDeviceLedgerPage(@Param("id") Long id); + + List deviceLedgerList(); + + List> selectWorkshopBatch(@Param("deviceIds") List deviceIds); + + List lineDeviceList(@Param("pageReqVO") LineDeviceRequestVO pageReqVO); + + + DeviceOperationStatusRespVO getDeviceOperationalStatus(); + + int selectIsReference(@Param("deviceId") Long deviceId); + + /** + * 根据goviewId查询对应的deviceIds + * + * @param goviewId 可视化大屏ID + * @return deviceIds字符串 + */ + @Select("SELECT device_ids FROM mes_goview WHERE id = #{goviewId}") + String selectDeviceIdsByGoviewId(@Param("goviewId") Long goviewId); + + + List selectLineBatch(@Param("deviceIds") List deviceIds); + + List selectDeviceIdsByLine(@Param("lineNode") String lineNode, @Param("lineName") String lineName); + + Integer getTotalDeviceCount(); + + List getAllDeviceIds(); +} \ No newline at end of file diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/mysql/device/IotDeviceGroupMapper.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/mysql/device/IotDeviceGroupMapper.java deleted file mode 100644 index 1f80ae4..0000000 --- a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/mysql/device/IotDeviceGroupMapper.java +++ /dev/null @@ -1,35 +0,0 @@ -package cn.iocoder.yudao.module.iot.dal.mysql.device; - -import cn.iocoder.yudao.framework.common.pojo.PageResult; -import cn.iocoder.yudao.framework.mybatis.core.mapper.BaseMapperX; -import cn.iocoder.yudao.framework.mybatis.core.query.LambdaQueryWrapperX; -import cn.iocoder.yudao.module.iot.controller.admin.device.vo.group.IotDeviceGroupPageReqVO; -import cn.iocoder.yudao.module.iot.dal.dataobject.device.IotDeviceGroupDO; -import org.apache.ibatis.annotations.Mapper; - -import java.util.List; - -/** - * IoT 设备分组 Mapper - * - * @author 芋道源码 - */ -@Mapper -public interface IotDeviceGroupMapper extends BaseMapperX { - - default PageResult selectPage(IotDeviceGroupPageReqVO reqVO) { - return selectPage(reqVO, new LambdaQueryWrapperX() - .likeIfPresent(IotDeviceGroupDO::getName, reqVO.getName()) - .betweenIfPresent(IotDeviceGroupDO::getCreateTime, reqVO.getCreateTime()) - .orderByDesc(IotDeviceGroupDO::getId)); - } - - default List selectListByStatus(Integer status) { - return selectList(IotDeviceGroupDO::getStatus, status); - } - - default IotDeviceGroupDO selectByName(String name) { - return selectOne(IotDeviceGroupDO::getName, name); - } - -} \ No newline at end of file diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/mysql/device/IotDeviceMapper.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/mysql/device/IotDeviceMapper.java deleted file mode 100644 index 8f8275c..0000000 --- a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/mysql/device/IotDeviceMapper.java +++ /dev/null @@ -1,175 +0,0 @@ -package cn.iocoder.yudao.module.iot.dal.mysql.device; - -import cn.hutool.core.util.ObjectUtil; -import cn.iocoder.yudao.framework.common.pojo.PageResult; -import cn.iocoder.yudao.framework.mybatis.core.mapper.BaseMapperX; -import cn.iocoder.yudao.framework.mybatis.core.query.LambdaQueryWrapperX; -import cn.iocoder.yudao.module.iot.controller.admin.device.vo.device.IotDevicePageReqVO; -import cn.iocoder.yudao.module.iot.dal.dataobject.device.IotDeviceDO; -import cn.iocoder.yudao.module.iot.enums.product.IotProductDeviceTypeEnum; -import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper; -import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper; -import org.apache.ibatis.annotations.Mapper; - -import javax.annotation.Nullable; -import java.time.LocalDateTime; -import java.util.Collection; -import java.util.List; -import java.util.Map; -import java.util.stream.Collectors; - -/** - * IoT 设备 Mapper - * - * @author 芋道源码 - */ -@Mapper -public interface IotDeviceMapper extends BaseMapperX { - - default PageResult selectPage(IotDevicePageReqVO reqVO) { - return selectPage(reqVO, new LambdaQueryWrapperX() - .likeIfPresent(IotDeviceDO::getDeviceName, reqVO.getDeviceName()) - .eqIfPresent(IotDeviceDO::getProductId, reqVO.getProductId()) - .eqIfPresent(IotDeviceDO::getDeviceType, reqVO.getDeviceType()) - .likeIfPresent(IotDeviceDO::getNickname, reqVO.getNickname()) - .eqIfPresent(IotDeviceDO::getState, reqVO.getStatus()) - .eqIfPresent(IotDeviceDO::getGatewayId, reqVO.getGatewayId()) - .apply(ObjectUtil.isNotNull(reqVO.getGroupId()), "FIND_IN_SET(" + reqVO.getGroupId() + ",group_ids) > 0") - .orderByDesc(IotDeviceDO::getId)); - } - - default IotDeviceDO selectByDeviceName(String deviceName) { - return selectOne(IotDeviceDO::getDeviceName, deviceName); - } - - default IotDeviceDO selectByProductKeyAndDeviceName(String productKey, String deviceName) { - return selectOne(IotDeviceDO::getProductKey, productKey, - IotDeviceDO::getDeviceName, deviceName); - } - - default long selectCountByGatewayId(Long id) { - return selectCount(IotDeviceDO::getGatewayId, id); - } - - default Long selectCountByProductId(Long productId) { - return selectCount(IotDeviceDO::getProductId, productId); - } - - default List selectListByCondition(@Nullable Integer deviceType, - @Nullable Long productId) { - return selectList(new LambdaQueryWrapperX() - .eqIfPresent(IotDeviceDO::getDeviceType, deviceType) - .eqIfPresent(IotDeviceDO::getProductId, productId)); - } - - default List selectListByState(Integer state) { - return selectList(IotDeviceDO::getState, state); - } - - default List selectListByProductId(Long productId) { - return selectList(IotDeviceDO::getProductId, productId); - } - - default Long selectCountByGroupId(Long groupId) { - return selectCount(new LambdaQueryWrapperX() - .apply("FIND_IN_SET(" + groupId + ",group_ids) > 0")); - } - - default Long selectCountByCreateTime(@Nullable LocalDateTime createTime) { - return selectCount(new LambdaQueryWrapperX() - .geIfPresent(IotDeviceDO::getCreateTime, createTime)); - } - - default List selectByProductKeyAndDeviceNames(String productKey, Collection deviceNames) { - return selectList(new LambdaQueryWrapperX() - .eq(IotDeviceDO::getProductKey, productKey) - .in(IotDeviceDO::getDeviceName, deviceNames)); - } - - default IotDeviceDO selectBySerialNumber(String serialNumber) { - return selectOne(IotDeviceDO::getSerialNumber, serialNumber); - } - - /** - * 查询指定产品下的设备数量 - * - * @return 产品编号 -> 设备数量的映射 - */ - default Map selectDeviceCountMapByProductId() { - List> result = selectMaps(new QueryWrapper() - .select("product_id AS productId", "COUNT(1) AS deviceCount") - .groupBy("product_id")); - return result.stream().collect(Collectors.toMap( - map -> Long.valueOf(map.get("productId").toString()), - map -> Integer.valueOf(map.get("deviceCount").toString()) - )); - } - - /** - * 查询各个状态下的设备数量 - * - * @return 设备状态 -> 设备数量的映射 - */ - default Map selectDeviceCountGroupByState() { - List> result = selectMaps(new QueryWrapper() - .select("state", "COUNT(1) AS deviceCount") - .groupBy("state")); - return result.stream().collect(Collectors.toMap( - map -> Integer.valueOf(map.get("state").toString()), - map -> Long.valueOf(map.get("deviceCount").toString()) - )); - } - - /** - * 查询有位置信息的设备列表 - * - * @return 设备列表 - */ - default List selectListByHasLocation() { - return selectList(new LambdaQueryWrapperX() - .isNotNull(IotDeviceDO::getLatitude) - .isNotNull(IotDeviceDO::getLongitude)); - } - - // ========== 网关-子设备绑定相关 ========== - - /** - * 根据网关编号查询子设备列表 - * - * @param gatewayId 网关设备编号 - * @return 子设备列表 - */ - default List selectListByGatewayId(Long gatewayId) { - return selectList(IotDeviceDO::getGatewayId, gatewayId); - } - - /** - * 分页查询未绑定网关的子设备 - * - * @param reqVO 分页查询参数 - * @return 子设备分页 - */ - default PageResult selectUnboundSubDevicePage(IotDevicePageReqVO reqVO) { - return selectPage(reqVO, new LambdaQueryWrapperX() - .likeIfPresent(IotDeviceDO::getDeviceName, reqVO.getDeviceName()) - .likeIfPresent(IotDeviceDO::getNickname, reqVO.getNickname()) - .eqIfPresent(IotDeviceDO::getProductId, reqVO.getProductId()) - // 仅查询子设备 + 未绑定网关 - .eq(IotDeviceDO::getDeviceType, IotProductDeviceTypeEnum.GATEWAY_SUB.getType()) - .isNull(IotDeviceDO::getGatewayId) - .orderByDesc(IotDeviceDO::getId)); - } - - /** - * 批量更新设备的网关编号 - * - * @param ids 设备编号列表 - * @param gatewayId 网关设备编号(可以为 null,表示解绑) - */ - default void updateGatewayIdBatch(Collection ids, Long gatewayId) { - update(null, new LambdaUpdateWrapper() - .set(IotDeviceDO::getGatewayId, gatewayId) - .in(IotDeviceDO::getId, ids)); - } - -} diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/mysql/device/IotDeviceModbusConfigMapper.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/mysql/device/IotDeviceModbusConfigMapper.java deleted file mode 100644 index b18769c..0000000 --- a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/mysql/device/IotDeviceModbusConfigMapper.java +++ /dev/null @@ -1,30 +0,0 @@ -package cn.iocoder.yudao.module.iot.dal.mysql.device; - -import cn.iocoder.yudao.framework.mybatis.core.mapper.BaseMapperX; -import cn.iocoder.yudao.framework.mybatis.core.query.LambdaQueryWrapperX; -import cn.iocoder.yudao.module.iot.core.biz.dto.IotModbusDeviceConfigListReqDTO; -import cn.iocoder.yudao.module.iot.dal.dataobject.device.IotDeviceModbusConfigDO; -import org.apache.ibatis.annotations.Mapper; - -import java.util.List; - -/** - * IoT 设备 Modbus 连接配置 Mapper - * - * @author 芋道源码 - */ -@Mapper -public interface IotDeviceModbusConfigMapper extends BaseMapperX { - - default IotDeviceModbusConfigDO selectByDeviceId(Long deviceId) { - return selectOne(IotDeviceModbusConfigDO::getDeviceId, deviceId); - } - - default List selectList(IotModbusDeviceConfigListReqDTO reqDTO) { - return selectList(new LambdaQueryWrapperX() - .eqIfPresent(IotDeviceModbusConfigDO::getStatus, reqDTO.getStatus()) - .eqIfPresent(IotDeviceModbusConfigDO::getMode, reqDTO.getMode()) - .inIfPresent(IotDeviceModbusConfigDO::getDeviceId, reqDTO.getDeviceIds())); - } - -} diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/mysql/device/IotDeviceModbusPointMapper.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/mysql/device/IotDeviceModbusPointMapper.java deleted file mode 100644 index 7c9b5d3..0000000 --- a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/mysql/device/IotDeviceModbusPointMapper.java +++ /dev/null @@ -1,47 +0,0 @@ -package cn.iocoder.yudao.module.iot.dal.mysql.device; - -import cn.iocoder.yudao.framework.common.pojo.PageResult; -import cn.iocoder.yudao.framework.mybatis.core.mapper.BaseMapperX; -import cn.iocoder.yudao.framework.mybatis.core.query.LambdaQueryWrapperX; -import cn.iocoder.yudao.module.iot.controller.admin.device.vo.modbus.IotDeviceModbusPointPageReqVO; -import cn.iocoder.yudao.module.iot.dal.dataobject.device.IotDeviceModbusPointDO; -import org.apache.ibatis.annotations.Mapper; - -import java.util.Collection; -import java.util.List; - -/** - * IoT 设备 Modbus 点位配置 Mapper - * - * @author 芋道源码 - */ -@Mapper -public interface IotDeviceModbusPointMapper extends BaseMapperX { - - default PageResult selectPage(IotDeviceModbusPointPageReqVO reqVO) { - return selectPage(reqVO, new LambdaQueryWrapperX() - .eqIfPresent(IotDeviceModbusPointDO::getDeviceId, reqVO.getDeviceId()) - .likeIfPresent(IotDeviceModbusPointDO::getIdentifier, reqVO.getIdentifier()) - .likeIfPresent(IotDeviceModbusPointDO::getName, reqVO.getName()) - .eqIfPresent(IotDeviceModbusPointDO::getFunctionCode, reqVO.getFunctionCode()) - .eqIfPresent(IotDeviceModbusPointDO::getStatus, reqVO.getStatus()) - .orderByDesc(IotDeviceModbusPointDO::getId)); - } - - default List selectListByDeviceIdsAndStatus(Collection deviceIds, Integer status) { - return selectList(new LambdaQueryWrapperX() - .in(IotDeviceModbusPointDO::getDeviceId, deviceIds) - .eq(IotDeviceModbusPointDO::getStatus, status)); - } - - default IotDeviceModbusPointDO selectByDeviceIdAndIdentifier(Long deviceId, String identifier) { - return selectOne(IotDeviceModbusPointDO::getDeviceId, deviceId, - IotDeviceModbusPointDO::getIdentifier, identifier); - } - - default void updateByThingModelId(Long thingModelId, IotDeviceModbusPointDO updateObj) { - update(updateObj, new LambdaQueryWrapperX() - .eq(IotDeviceModbusPointDO::getThingModelId, thingModelId)); - } - -} diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/mysql/deviceattributetype/DeviceAttributeTypeMapper.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/mysql/deviceattributetype/DeviceAttributeTypeMapper.java new file mode 100644 index 0000000..18ff9cd --- /dev/null +++ b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/mysql/deviceattributetype/DeviceAttributeTypeMapper.java @@ -0,0 +1,51 @@ +package cn.iocoder.yudao.module.iot.dal.mysql.deviceattributetype; + +import cn.iocoder.yudao.framework.common.pojo.PageResult; +import cn.iocoder.yudao.framework.mybatis.core.mapper.BaseMapperX; +import cn.iocoder.yudao.framework.mybatis.core.query.LambdaQueryWrapperX; +import cn.iocoder.yudao.module.iot.controller.admin.deviceattributetype.vo.*; +import cn.iocoder.yudao.module.iot.dal.dataobject.deviceattributetype.DeviceAttributeTypeDO; +import com.alibaba.excel.util.StringUtils; +import org.apache.ibatis.annotations.Mapper; + +import java.util.Arrays; +import java.util.List; +import java.util.stream.Collectors; + +/** + * 采集点分类 Mapper + * + * @author 内蒙必硕 + */ +@Mapper +public interface DeviceAttributeTypeMapper extends BaseMapperX { + + + default PageResult selectPage(DeviceAttributeTypePageReqVO reqVO) { + + + LambdaQueryWrapperX deviceAttributeTypeDOLambdaQueryWrapperX = new LambdaQueryWrapperX<>(); + deviceAttributeTypeDOLambdaQueryWrapperX + .eqIfPresent(DeviceAttributeTypeDO::getCode, reqVO.getCode()) + .likeIfPresent(DeviceAttributeTypeDO::getName, reqVO.getName()) + .eqIfPresent(DeviceAttributeTypeDO::getRemark, reqVO.getRemark()) + .betweenIfPresent(DeviceAttributeTypeDO::getCreateTime, reqVO.getCreateTime()) + .orderByAsc(DeviceAttributeTypeDO::getSort); + + if (StringUtils.isNotBlank(reqVO.getIds())) { + List idList = Arrays.stream(reqVO.getIds().split(",")) + .map(String::trim) + .map(Long::valueOf) + .collect(Collectors.toList()); + deviceAttributeTypeDOLambdaQueryWrapperX.in(DeviceAttributeTypeDO::getId, idList); + } + + + return selectPage(reqVO, deviceAttributeTypeDOLambdaQueryWrapperX); + + } + + default List select() { + return selectList(); + } +} \ No newline at end of file diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/mysql/devicecontactmodel/DeviceContactModelMapper.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/mysql/devicecontactmodel/DeviceContactModelMapper.java new file mode 100644 index 0000000..09a9a4c --- /dev/null +++ b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/mysql/devicecontactmodel/DeviceContactModelMapper.java @@ -0,0 +1,75 @@ +package cn.iocoder.yudao.module.iot.dal.mysql.devicecontactmodel; + +import cn.iocoder.yudao.framework.common.pojo.PageParam; +import cn.iocoder.yudao.framework.common.pojo.PageResult; +import cn.iocoder.yudao.framework.mybatis.core.mapper.BaseMapperX; +import cn.iocoder.yudao.framework.mybatis.core.query.LambdaQueryWrapperX; +import cn.iocoder.yudao.module.iot.controller.admin.devicecontactmodel.vo.*; +import cn.iocoder.yudao.module.iot.dal.dataobject.devicecontactmodel.DeviceContactModelDO; +import com.alibaba.excel.util.StringUtils; +import org.apache.ibatis.annotations.Mapper; +import org.apache.ibatis.annotations.Param; + +import java.util.Arrays; +import java.util.List; +import java.util.Map; +import java.util.Objects; +import java.util.stream.Collectors; + +/** + * 采集设备模型-点位管理 Mapper + * + * @author 内蒙必硕 + */ +@Mapper +public interface DeviceContactModelMapper extends BaseMapperX { + + default PageResult selectPageById(PageParam reqVO, DeviceContactModelPageReqVO device) { + return selectPage(reqVO, new LambdaQueryWrapperX() + // 使用StringUtils.hasText()进行更严格的字符串非空判断 + .like(device.getAttributeCode() != null && StringUtils.isNotBlank(device.getAttributeCode()), DeviceContactModelDO::getAttributeCode, device.getAttributeCode()) + .like(device.getAttributeName() != null && StringUtils.isNotBlank(device.getAttributeName()), DeviceContactModelDO::getAttributeName, device.getAttributeName()) + // 对于枚举或数值类型,使用Objects.nonNull()判断 + .eq(Objects.nonNull(device.getAttributeType()), DeviceContactModelDO::getAttributeType, device.getAttributeType()) + // 必要的条件(如deviceModelId)保留直接eq条件 + .eq(DeviceContactModelDO::getDeviceId, device.getDeviceId()) + .orderByDesc(DeviceContactModelDO::getId)); + } + + + default PageResult selectPage(DeviceContactModelPageReqVO reqVO) { + + LambdaQueryWrapperX deviceContactModelDOLambdaQueryWrapperX = new LambdaQueryWrapperX<>(); + deviceContactModelDOLambdaQueryWrapperX.eqIfPresent(DeviceContactModelDO::getAttributeCode, reqVO.getAttributeCode()) + .likeIfPresent(DeviceContactModelDO::getAttributeName, reqVO.getAttributeName()) + .eqIfPresent(DeviceContactModelDO::getAttributeType, reqVO.getAttributeType()) + .eqIfPresent(DeviceContactModelDO::getDataType, reqVO.getDataType()) + .eqIfPresent(DeviceContactModelDO::getAddress, reqVO.getAddress()) + .eqIfPresent(DeviceContactModelDO::getDataUnit, reqVO.getDataUnit()) + .eqIfPresent(DeviceContactModelDO::getRatio, reqVO.getRatio()) + .eqIfPresent(DeviceContactModelDO::getRemark, reqVO.getRemark()) + .eqIfPresent(DeviceContactModelDO::getDeviceId, reqVO.getDeviceId()) + .betweenIfPresent(DeviceContactModelDO::getCreateTime, reqVO.getCreateTime()); + + if (reqVO.getIds()!=null && StringUtils.isNotBlank(reqVO.getIds())) { + List idList = Arrays.stream(reqVO.getIds().split(",")) + .map(String::trim) + .map(Long::valueOf) + .collect(Collectors.toList()); + deviceContactModelDOLambdaQueryWrapperX.in(DeviceContactModelDO::getId, idList); + } + + PageResult deviceContactModelDOPageResult = selectPage(reqVO, deviceContactModelDOLambdaQueryWrapperX); + + return deviceContactModelDOPageResult; + } + + /** + * 根据ID查询属性名和数据单位 + * @param id 属性ID + * @return 包含attribute_name和data_unit的Map + */ + Map selectAttributeNameAndUnitById(@Param("id") Long id); + + +} \ No newline at end of file diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/mysql/devicemodel/DeviceModelMapper.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/mysql/devicemodel/DeviceModelMapper.java new file mode 100644 index 0000000..5bded44 --- /dev/null +++ b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/mysql/devicemodel/DeviceModelMapper.java @@ -0,0 +1,53 @@ +package cn.iocoder.yudao.module.iot.dal.mysql.devicemodel; + +import cn.iocoder.yudao.framework.common.pojo.PageResult; +import cn.iocoder.yudao.framework.mybatis.core.mapper.BaseMapperX; +import cn.iocoder.yudao.framework.mybatis.core.query.LambdaQueryWrapperX; +import cn.iocoder.yudao.module.iot.controller.admin.devicemodel.vo.DeviceModelPageReqVO; +import cn.iocoder.yudao.module.iot.dal.dataobject.devicemodel.DeviceModelDO; +import com.alibaba.excel.util.StringUtils; +import org.apache.ibatis.annotations.Mapper; + +import java.util.Arrays; +import java.util.List; +import java.util.stream.Collectors; + +/** + * 采集设备模型 Mapper + * + * @author 内蒙必硕 + */ +@Mapper +public interface DeviceModelMapper extends BaseMapperX { + + default PageResult selectPage(DeviceModelPageReqVO reqVO) { + + LambdaQueryWrapperX deviceModelDOLambdaQueryWrapperX = new LambdaQueryWrapperX<>(); + deviceModelDOLambdaQueryWrapperX.eqIfPresent(DeviceModelDO::getCode, reqVO.getCode()) + .likeIfPresent(DeviceModelDO::getName, reqVO.getName()) + .eqIfPresent(DeviceModelDO::getProtocol, reqVO.getProtocol()) + .eqIfPresent(DeviceModelDO::getRemark, reqVO.getRemark()) + .betweenIfPresent(DeviceModelDO::getCreateTime, reqVO.getCreateTime()) + .orderByDesc(DeviceModelDO::getCreateTime); + + + // 单独处理 ids 条件 + if (StringUtils.isNotBlank(reqVO.getIds())) { + List idList = Arrays.stream(reqVO.getIds().split(",")) + .map(String::trim) + .map(Long::valueOf) + .collect(Collectors.toList()); + deviceModelDOLambdaQueryWrapperX.in(DeviceModelDO::getId, idList); + } + + + return selectPage(reqVO, deviceModelDOLambdaQueryWrapperX); + + + } + + default List select() { + return selectList(); + } + +} \ No newline at end of file diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/mysql/devicemodelattribute/DeviceModelAttributeMapper.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/mysql/devicemodelattribute/DeviceModelAttributeMapper.java new file mode 100644 index 0000000..5965f2c --- /dev/null +++ b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/mysql/devicemodelattribute/DeviceModelAttributeMapper.java @@ -0,0 +1,72 @@ +package cn.iocoder.yudao.module.iot.dal.mysql.devicemodelattribute; + +import cn.iocoder.yudao.framework.common.pojo.PageParam; +import cn.iocoder.yudao.framework.common.pojo.PageResult; +import cn.iocoder.yudao.framework.mybatis.core.mapper.BaseMapperX; +import cn.iocoder.yudao.framework.mybatis.core.query.LambdaQueryWrapperX; +import cn.iocoder.yudao.module.iot.controller.admin.devicemodelattribute.vo.DeviceModelAttributePageReqVO; +import cn.iocoder.yudao.module.iot.dal.dataobject.devicemodelattribute.DeviceModelAttributeDO; +import com.alibaba.excel.util.StringUtils; +import org.apache.ibatis.annotations.Mapper; + +import java.util.Arrays; +import java.util.List; +import java.util.Objects; +import java.util.stream.Collectors; + +/** + * 采集设备模型-点位管理 Mapper + * + * @author 内蒙必硕 + */ +@Mapper +public interface DeviceModelAttributeMapper extends BaseMapperX { + + + default PageResult selectPageById(PageParam reqVO, DeviceModelAttributePageReqVO device) { + return selectPage(reqVO, new LambdaQueryWrapperX() + // 使用StringUtils.hasText()进行更严格的字符串非空判断 + .like(StringUtils.isNotBlank(device.getAttributeCode()), DeviceModelAttributeDO::getAttributeCode, device.getAttributeCode()) + .like(StringUtils.isNotBlank(device.getAttributeName()), DeviceModelAttributeDO::getAttributeName, device.getAttributeName()) + // 对于枚举或数值类型,使用Objects.nonNull()判断 + .eq(Objects.nonNull(device.getAttributeType()), DeviceModelAttributeDO::getAttributeType, device.getAttributeType()) + // 必要的条件(如deviceModelId)保留直接eq条件 + .eq(DeviceModelAttributeDO::getDeviceModelId, device.getDeviceModelId()) + .orderByDesc(DeviceModelAttributeDO::getId)); + } + + + default PageResult selectPage(DeviceModelAttributePageReqVO reqVO) { + + LambdaQueryWrapperX deviceModelAttributeDOLambdaQueryWrapperX = new LambdaQueryWrapperX<>(); + deviceModelAttributeDOLambdaQueryWrapperX.eqIfPresent(DeviceModelAttributeDO::getAttributeCode, reqVO.getAttributeCode()) + .likeIfPresent(DeviceModelAttributeDO::getAttributeName, reqVO.getAttributeName()) + .eqIfPresent(DeviceModelAttributeDO::getAttributeType, reqVO.getAttributeType()) + .eqIfPresent(DeviceModelAttributeDO::getDataType, reqVO.getDataType()) + .eqIfPresent(DeviceModelAttributeDO::getAddress, reqVO.getAddress()) + .eqIfPresent(DeviceModelAttributeDO::getDataUnit, reqVO.getDataUnit()) + .eqIfPresent(DeviceModelAttributeDO::getRatio, reqVO.getRatio()) + .eqIfPresent(DeviceModelAttributeDO::getRemark, reqVO.getRemark()) + .eqIfPresent(DeviceModelAttributeDO::getDeviceModelId, reqVO.getId()) + .betweenIfPresent(DeviceModelAttributeDO::getCreateTime, reqVO.getCreateTime()) + .orderByDesc(DeviceModelAttributeDO::getId); + + + if(reqVO.getIds() !=null && StringUtils.isNotBlank(reqVO.getIds())){ + List idList = Arrays.stream(reqVO.getIds().split(",")) + .map(String::trim) + .map(Long::valueOf) + .collect(Collectors.toList()); + deviceModelAttributeDOLambdaQueryWrapperX.in(DeviceModelAttributeDO::getId,idList); + } + + PageResult deviceModelAttributeDOPageResult = selectPage(reqVO, deviceModelAttributeDOLambdaQueryWrapperX); + return deviceModelAttributeDOPageResult; + + } + + default DeviceModelAttributeDO selectByAttributeCode(String attributeCode) { + return selectOne(DeviceModelAttributeDO::getAttributeCode, attributeCode); + } + +} \ No newline at end of file diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/mysql/devicemodelrules/DeviceModelRulesDO.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/mysql/devicemodelrules/DeviceModelRulesDO.java new file mode 100644 index 0000000..968811d --- /dev/null +++ b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/mysql/devicemodelrules/DeviceModelRulesDO.java @@ -0,0 +1,55 @@ +package cn.iocoder.yudao.module.iot.dal.mysql.devicemodelrules; + +import cn.iocoder.yudao.framework.mybatis.core.dataobject.BaseDO; +import com.baomidou.mybatisplus.annotation.KeySequence; +import com.baomidou.mybatisplus.annotation.TableId; +import com.baomidou.mybatisplus.annotation.TableName; +import lombok.*; + +/** + * 点位规则关联模型 DO + * + * @author 必硕科技 + */ +@TableName("iot_device_model_rules") +@KeySequence("iot_device_model_rules_seq") // 用于 Oracle、PostgreSQL、Kingbase、DB2、H2 数据库的主键自增。如果是 MySQL 等数据库,可不写。 +@Data +@EqualsAndHashCode(callSuper = true) +@ToString(callSuper = true) +@Builder +@NoArgsConstructor +@AllArgsConstructor +public class DeviceModelRulesDO extends BaseDO { + + /** + * ID + */ + @TableId + private Long id; + /** + * 标识符 + */ + private String identifier; + /** + * 名称 + */ + private String fieldName; + /** + * 规则 + */ + private String fieldRule; + /** + * 默认值 + */ + private String defaultValue; + /** + * 关联设备模型Id + */ + private Long modelId; + + /** + * 告警等级 + */ + private String alarmLevel; + +} \ No newline at end of file diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/mysql/devicemodelrules/DeviceModelRulesMapper.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/mysql/devicemodelrules/DeviceModelRulesMapper.java new file mode 100644 index 0000000..a20c855 --- /dev/null +++ b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/mysql/devicemodelrules/DeviceModelRulesMapper.java @@ -0,0 +1,29 @@ +package cn.iocoder.yudao.module.iot.dal.mysql.devicemodelrules; + +import cn.iocoder.yudao.framework.common.pojo.PageResult; +import cn.iocoder.yudao.framework.mybatis.core.mapper.BaseMapperX; +import cn.iocoder.yudao.framework.mybatis.core.query.LambdaQueryWrapperX; +import cn.iocoder.yudao.module.iot.controller.admin.devicemodelrules.vo.DeviceModelRulesPageReqVO; +import cn.iocoder.yudao.module.iot.dal.dataobject.devicemodelrules.DeviceModelRulesDO; +import org.apache.ibatis.annotations.Mapper; + +/** + * 点位规则关联模型 Mapper + * + * @author 必硕科技 + */ +@Mapper +public interface DeviceModelRulesMapper extends BaseMapperX { + + default PageResult selectPage(DeviceModelRulesPageReqVO reqVO) { + return selectPage(reqVO, new LambdaQueryWrapperX() + .eqIfPresent(DeviceModelRulesDO::getIdentifier, reqVO.getIdentifier()) + .likeIfPresent(DeviceModelRulesDO::getFieldName, reqVO.getFieldName()) + .eqIfPresent(DeviceModelRulesDO::getFieldRule, reqVO.getFieldRule()) + .eqIfPresent(DeviceModelRulesDO::getDefaultValue, reqVO.getDefaultValue()) + .betweenIfPresent(DeviceModelRulesDO::getCreateTime, reqVO.getCreateTime()) + .eqIfPresent(DeviceModelRulesDO::getModelId, reqVO.getModelId()) + .orderByDesc(DeviceModelRulesDO::getId)); + } + +} \ No newline at end of file diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/mysql/deviceoperationrecord/DeviceOperationRecordMapper.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/mysql/deviceoperationrecord/DeviceOperationRecordMapper.java new file mode 100644 index 0000000..c11ff15 --- /dev/null +++ b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/mysql/deviceoperationrecord/DeviceOperationRecordMapper.java @@ -0,0 +1,59 @@ +package cn.iocoder.yudao.module.iot.dal.mysql.deviceoperationrecord; + +import java.util.*; + +import cn.iocoder.yudao.framework.common.pojo.PageResult; +import cn.iocoder.yudao.framework.mybatis.core.query.LambdaQueryWrapperX; +import cn.iocoder.yudao.framework.mybatis.core.mapper.BaseMapperX; +import cn.iocoder.yudao.module.iot.dal.dataobject.deviceoperationrecord.DeviceOperationRecordDO; +import com.baomidou.dynamic.datasource.annotation.DS; +import com.baomidou.mybatisplus.core.metadata.IPage; +import com.baomidou.mybatisplus.extension.plugins.pagination.Page; +import org.apache.ibatis.annotations.Mapper; +import cn.iocoder.yudao.module.iot.controller.admin.deviceoperationrecord.vo.*; +import org.apache.ibatis.annotations.Param; + +/** + * 运行记录 Mapper + * + * @author 必硕科技 + */ +@Mapper +public interface DeviceOperationRecordMapper extends BaseMapperX { + + default PageResult selectPage(DeviceOperationRecordPageReqVO reqVO) { + return selectPage(reqVO, new LambdaQueryWrapperX() + .eqIfPresent(DeviceOperationRecordDO::getDeviceId, reqVO.getDeviceId()) + .eqIfPresent(DeviceOperationRecordDO::getModelId, reqVO.getModelId()) + .eqIfPresent(DeviceOperationRecordDO::getRule, reqVO.getRule()) + .eqIfPresent(DeviceOperationRecordDO::getAddressValue, reqVO.getAddressValue()) + .betweenIfPresent(DeviceOperationRecordDO::getCreateTime, reqVO.getCreateTime()) + .eqIfPresent(DeviceOperationRecordDO::getRecordType, reqVO.getRecordType()) + .orderByDesc(DeviceOperationRecordDO::getId)); + } + + IPage deviceOperationPage(Page page,@Param("pageReqVO") DeviceTotalTimeRecordReqVO pageReqVO); + + List selectLatestByDeviceAndRule(@Param("deviceIds") List deviceIds, @Param("ruleCodes") List ruleCodes); + + + List selectLatestRecords(@Param("deviceIds") Collection deviceIds); + + List selectLine(); + + List deviceOperationList(@Param("deviceTotalTimeRecordReqVO") DeviceTotalTimeRecordReqVO deviceTotalTimeRecordReqVO); + + IPage selectDevicePageFromMySQL( + Page page, + @Param("pageReqVO") DeviceTotalTimeRecordReqVO pageReqVO + ); + + @DS("tdengine") + List> selectDeviceStatsFromTD( + @Param("deviceIds") List deviceIds, + @Param("startTime") String startTime, + @Param("endTime") String endTime + ); + + List selectDeviceListFromMySQL(@Param("deviceTotalTimeRecordReqVO") DeviceTotalTimeRecordReqVO reqVO); +} \ No newline at end of file diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/mysql/devicepointrules/DevicePointRulesMapper.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/mysql/devicepointrules/DevicePointRulesMapper.java new file mode 100644 index 0000000..cf2086f --- /dev/null +++ b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/mysql/devicepointrules/DevicePointRulesMapper.java @@ -0,0 +1,29 @@ +package cn.iocoder.yudao.module.iot.dal.mysql.devicepointrules; + +import cn.iocoder.yudao.framework.common.pojo.PageResult; +import cn.iocoder.yudao.framework.mybatis.core.mapper.BaseMapperX; +import cn.iocoder.yudao.framework.mybatis.core.query.LambdaQueryWrapperX; +import cn.iocoder.yudao.module.iot.controller.admin.devicepointrules.vo.*; +import cn.iocoder.yudao.module.iot.dal.dataobject.devicepointrules.DevicePointRulesDO; +import org.apache.ibatis.annotations.Mapper; + +/** + * 点位规则 Mapper + * + * @author 必硕科技 + */ +@Mapper +public interface DevicePointRulesMapper extends BaseMapperX { + + default PageResult selectPage(DevicePointRulesPageReqVO reqVO) { + return selectPage(reqVO, new LambdaQueryWrapperX() + .eqIfPresent(DevicePointRulesDO::getIdentifier, reqVO.getIdentifier()) + .likeIfPresent(DevicePointRulesDO::getFieldName, reqVO.getFieldName()) + .eqIfPresent(DevicePointRulesDO::getFieldRule, reqVO.getFieldRule()) + .eqIfPresent(DevicePointRulesDO::getDefaultValue, reqVO.getDefaultValue()) + .betweenIfPresent(DevicePointRulesDO::getCreateTime, reqVO.getCreateTime()) + .eqIfPresent(DevicePointRulesDO::getDeviceId, reqVO.getDeviceId()) + .orderByDesc(DevicePointRulesDO::getId)); + } + +} \ No newline at end of file diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/mysql/devicewarinningrecord/DeviceWarinningRecordMapper.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/mysql/devicewarinningrecord/DeviceWarinningRecordMapper.java new file mode 100644 index 0000000..8227665 --- /dev/null +++ b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/mysql/devicewarinningrecord/DeviceWarinningRecordMapper.java @@ -0,0 +1,34 @@ +package cn.iocoder.yudao.module.iot.dal.mysql.devicewarinningrecord; + +import cn.iocoder.yudao.framework.common.pojo.PageResult; +import cn.iocoder.yudao.framework.mybatis.core.mapper.BaseMapperX; +import cn.iocoder.yudao.framework.mybatis.core.query.LambdaQueryWrapperX; +import cn.iocoder.yudao.module.iot.controller.admin.devicewarinningrecord.vo.DeviceWarinningRecordPageReqVO; +import cn.iocoder.yudao.module.iot.dal.dataobject.devicewarinningrecord.DeviceWarinningRecordDO; +import org.apache.ibatis.annotations.Mapper; + +import java.util.List; +import java.util.Map; + +/** + * 告警记录 Mapper + * + * @author 必硕科技 + */ +@Mapper +public interface DeviceWarinningRecordMapper extends BaseMapperX { + + default PageResult selectPage(DeviceWarinningRecordPageReqVO reqVO) { + return selectPage(reqVO, new LambdaQueryWrapperX() + .eqIfPresent(DeviceWarinningRecordDO::getDeviceId, reqVO.getDeviceId()) + .eqIfPresent(DeviceWarinningRecordDO::getModelId, reqVO.getModelId()) + .eqIfPresent(DeviceWarinningRecordDO::getRule, reqVO.getRule()) + .eqIfPresent(DeviceWarinningRecordDO::getAlarmLevel, reqVO.getAlarmLevel()) + .eqIfPresent(DeviceWarinningRecordDO::getAddressValue, reqVO.getAddressValue()) + .betweenIfPresent(DeviceWarinningRecordDO::getCreateTime, reqVO.getCreateTime()) + .eqIfPresent(DeviceWarinningRecordDO::getRuleId, reqVO.getRuleId()) + .orderByDesc(DeviceWarinningRecordDO::getId)); + } + + List> getLastSevenHoursCount(); +} \ No newline at end of file diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/mysql/gateway/GatewayMapper.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/mysql/gateway/GatewayMapper.java new file mode 100644 index 0000000..c386416 --- /dev/null +++ b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/mysql/gateway/GatewayMapper.java @@ -0,0 +1,39 @@ +package cn.iocoder.yudao.module.iot.dal.mysql.gateway; + +import cn.iocoder.yudao.framework.common.pojo.PageResult; +import cn.iocoder.yudao.framework.mybatis.core.mapper.BaseMapperX; +import cn.iocoder.yudao.framework.mybatis.core.query.LambdaQueryWrapperX; +import cn.iocoder.yudao.module.iot.controller.admin.gateway.vo.GatewayPageReqVO; +import cn.iocoder.yudao.module.iot.dal.dataobject.gateway.GatewayDO; +import org.apache.ibatis.annotations.Mapper; + +import java.util.List; + +/** + * 网关 Mapper + * + * @author 内蒙必硕 + */ +@Mapper +public interface GatewayMapper extends BaseMapperX { + + default PageResult selectPage(GatewayPageReqVO reqVO) { + return selectPage(reqVO, new LambdaQueryWrapperX() + .eqIfPresent(GatewayDO::getAdminIp, reqVO.getAdminIp()) + .likeIfPresent(GatewayDO::getUsername, reqVO.getUsername()) + .eqIfPresent(GatewayDO::getPassword, reqVO.getPassword()) + .likeIfPresent(GatewayDO::getGatewayName, reqVO.getGatewayName()) + .eqIfPresent(GatewayDO::getRemark, reqVO.getRemark()) + .eqIfPresent(GatewayDO::getIsEnable, reqVO.getIsEnable()) + .betweenIfPresent(GatewayDO::getCreateTime, reqVO.getCreateTime()) + .eqIfPresent(GatewayDO::getGatewayCode, reqVO.getGatewayCode()) + .eqIfPresent(GatewayDO::getGatewayStatus, reqVO.getGatewayStatus()) + .eqIfPresent(GatewayDO::getTopic, reqVO.getTopic()) + .orderByDesc(GatewayDO::getId)); + } + + default List selectListByIsEnable(boolean isEnable) { + return selectList(GatewayDO::getIsEnable, isEnable); + } + +} \ No newline at end of file diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/mysql/orgnode/OrgNodeMapper.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/mysql/orgnode/OrgNodeMapper.java new file mode 100644 index 0000000..a3546e4 --- /dev/null +++ b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/mysql/orgnode/OrgNodeMapper.java @@ -0,0 +1,34 @@ +package cn.iocoder.yudao.module.iot.dal.mysql.orgnode; + +import java.util.*; + +import cn.iocoder.yudao.framework.common.pojo.PageResult; +import cn.iocoder.yudao.framework.mybatis.core.query.LambdaQueryWrapperX; +import cn.iocoder.yudao.framework.mybatis.core.mapper.BaseMapperX; +import cn.iocoder.yudao.module.iot.dal.dataobject.orgnode.OrgNodeDO; +import org.apache.ibatis.annotations.Mapper; +import cn.iocoder.yudao.module.iot.controller.admin.orgnode.vo.*; +import org.apache.ibatis.annotations.Param; + +/** + * 客户组织节点表,用于管理客户、车间、产线等多级组织结构 Mapper + * + * @author 管理员 + */ +@Mapper +public interface OrgNodeMapper extends BaseMapperX { + + default PageResult selectPage(OrgNodePageReqVO reqVO) { + return selectPage(reqVO, new LambdaQueryWrapperX() + .eqIfPresent(OrgNodeDO::getCustomerId, reqVO.getCustomerId()) + .eqIfPresent(OrgNodeDO::getParentId, reqVO.getParentId()) + .eqIfPresent(OrgNodeDO::getNodeType, reqVO.getNodeType()) + .likeIfPresent(OrgNodeDO::getName, reqVO.getName()) + .eqIfPresent(OrgNodeDO::getSort, reqVO.getSort()) + .betweenIfPresent(OrgNodeDO::getCreateTime, reqVO.getCreateTime()) + .orderByDesc(OrgNodeDO::getId)); + } + List selectListByTenantAndCustomer(@Param("tenantId") Long tenantId, + @Param("customerId") Long customerId); + +} \ No newline at end of file diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/mysql/ota/IotOtaFirmwareMapper.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/mysql/ota/IotOtaFirmwareMapper.java deleted file mode 100644 index fea6272..0000000 --- a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/mysql/ota/IotOtaFirmwareMapper.java +++ /dev/null @@ -1,26 +0,0 @@ -package cn.iocoder.yudao.module.iot.dal.mysql.ota; - -import cn.iocoder.yudao.framework.common.pojo.PageResult; -import cn.iocoder.yudao.framework.mybatis.core.mapper.BaseMapperX; -import cn.iocoder.yudao.framework.mybatis.core.query.LambdaQueryWrapperX; -import cn.iocoder.yudao.module.iot.controller.admin.ota.vo.firmware.IotOtaFirmwarePageReqVO; -import cn.iocoder.yudao.module.iot.dal.dataobject.ota.IotOtaFirmwareDO; -import org.apache.ibatis.annotations.Mapper; - -@Mapper -public interface IotOtaFirmwareMapper extends BaseMapperX { - - default IotOtaFirmwareDO selectByProductIdAndVersion(Long productId, String version) { - return selectOne(IotOtaFirmwareDO::getProductId, productId, - IotOtaFirmwareDO::getVersion, version); - } - - default PageResult selectPage(IotOtaFirmwarePageReqVO pageReqVO) { - return selectPage(pageReqVO, new LambdaQueryWrapperX() - .likeIfPresent(IotOtaFirmwareDO::getName, pageReqVO.getName()) - .eqIfPresent(IotOtaFirmwareDO::getProductId, pageReqVO.getProductId()) - .betweenIfPresent(IotOtaFirmwareDO::getCreateTime, pageReqVO.getCreateTime()) - .orderByDesc(IotOtaFirmwareDO::getCreateTime)); - } - -} diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/mysql/ota/IotOtaTaskMapper.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/mysql/ota/IotOtaTaskMapper.java deleted file mode 100644 index cf73231..0000000 --- a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/mysql/ota/IotOtaTaskMapper.java +++ /dev/null @@ -1,32 +0,0 @@ -package cn.iocoder.yudao.module.iot.dal.mysql.ota; - -import cn.iocoder.yudao.framework.common.pojo.PageResult; -import cn.iocoder.yudao.framework.mybatis.core.mapper.BaseMapperX; -import cn.iocoder.yudao.framework.mybatis.core.query.LambdaQueryWrapperX; -import cn.iocoder.yudao.module.iot.controller.admin.ota.vo.task.IotOtaTaskPageReqVO; -import cn.iocoder.yudao.module.iot.dal.dataobject.ota.IotOtaTaskDO; -import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper; -import org.apache.ibatis.annotations.Mapper; - -@Mapper -public interface IotOtaTaskMapper extends BaseMapperX { - - default IotOtaTaskDO selectByFirmwareIdAndName(Long firmwareId, String name) { - return selectOne(IotOtaTaskDO::getFirmwareId, firmwareId, - IotOtaTaskDO::getName, name); - } - - default PageResult selectPage(IotOtaTaskPageReqVO pageReqVO) { - return selectPage(pageReqVO, new LambdaQueryWrapperX() - .eqIfPresent(IotOtaTaskDO::getFirmwareId, pageReqVO.getFirmwareId()) - .likeIfPresent(IotOtaTaskDO::getName, pageReqVO.getName()) - .orderByDesc(IotOtaTaskDO::getId)); - } - - default int updateByIdAndStatus(Long id, Integer whereStatus, IotOtaTaskDO updateObj) { - return update(updateObj, new LambdaUpdateWrapper() - .eq(IotOtaTaskDO::getId, id) - .eq(IotOtaTaskDO::getStatus, whereStatus)); - } - -} diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/mysql/ota/IotOtaTaskRecordMapper.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/mysql/ota/IotOtaTaskRecordMapper.java deleted file mode 100644 index 017adc9..0000000 --- a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/mysql/ota/IotOtaTaskRecordMapper.java +++ /dev/null @@ -1,80 +0,0 @@ -package cn.iocoder.yudao.module.iot.dal.mysql.ota; - -import cn.iocoder.yudao.framework.common.pojo.PageResult; -import cn.iocoder.yudao.framework.mybatis.core.mapper.BaseMapperX; -import cn.iocoder.yudao.framework.mybatis.core.query.LambdaQueryWrapperX; -import cn.iocoder.yudao.module.iot.controller.admin.ota.vo.task.record.IotOtaTaskRecordPageReqVO; -import cn.iocoder.yudao.module.iot.dal.dataobject.ota.IotOtaTaskRecordDO; -import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper; -import org.apache.ibatis.annotations.Mapper; - -import java.util.Collection; -import java.util.List; -import java.util.Set; - -@Mapper -public interface IotOtaTaskRecordMapper extends BaseMapperX { - - default List selectListByFirmwareIdAndTaskId(Long firmwareId, Long taskId) { - return selectList(new LambdaQueryWrapperX() - .eqIfPresent(IotOtaTaskRecordDO::getFirmwareId, firmwareId) - .eqIfPresent(IotOtaTaskRecordDO::getTaskId, taskId) - .select(IotOtaTaskRecordDO::getDeviceId, IotOtaTaskRecordDO::getStatus)); - } - - default PageResult selectPage(IotOtaTaskRecordPageReqVO pageReqVO) { - return selectPage(pageReqVO, new LambdaQueryWrapperX() - .eqIfPresent(IotOtaTaskRecordDO::getTaskId, pageReqVO.getTaskId()) - .eqIfPresent(IotOtaTaskRecordDO::getStatus, pageReqVO.getStatus())); - } - - default List selectListByTaskIdAndStatus(Long taskId, Collection statuses) { - return selectList(new LambdaQueryWrapperX() - .eq(IotOtaTaskRecordDO::getTaskId, taskId) - .in(IotOtaTaskRecordDO::getStatus, statuses)); - } - - default Long selectCountByTaskIdAndStatus(Long taskId, Collection statuses) { - return selectCount(new LambdaQueryWrapperX() - .eq(IotOtaTaskRecordDO::getTaskId, taskId) - .in(IotOtaTaskRecordDO::getStatus, statuses)); - } - - default int updateByIdAndStatus(Long id, Integer status, - IotOtaTaskRecordDO updateObj) { - return update(updateObj, new LambdaUpdateWrapper() - .eq(IotOtaTaskRecordDO::getId, id) - .eq(IotOtaTaskRecordDO::getStatus, status)); - } - - default int updateByIdAndStatus(Long id, Collection whereStatuses, - IotOtaTaskRecordDO updateObj) { - return update(updateObj, new LambdaUpdateWrapper() - .eq(IotOtaTaskRecordDO::getId, id) - .in(IotOtaTaskRecordDO::getStatus, whereStatuses)); - } - - default void updateListByIdAndStatus(Collection ids, Collection whereStatuses, - IotOtaTaskRecordDO updateObj) { - update(updateObj, new LambdaUpdateWrapper() - .in(IotOtaTaskRecordDO::getId, ids) - .in(IotOtaTaskRecordDO::getStatus, whereStatuses)); - } - - default List selectListByDeviceIdAndStatus(Set deviceIds, Set statuses) { - return selectList(new LambdaQueryWrapperX() - .inIfPresent(IotOtaTaskRecordDO::getDeviceId, deviceIds) - .inIfPresent(IotOtaTaskRecordDO::getStatus, statuses)); - } - - default List selectListByDeviceIdAndStatus(Long deviceId, Set statuses) { - return selectList(new LambdaQueryWrapperX() - .eqIfPresent(IotOtaTaskRecordDO::getDeviceId, deviceId) - .inIfPresent(IotOtaTaskRecordDO::getStatus, statuses)); - } - - default List selectListByStatus(Integer status) { - return selectList(IotOtaTaskRecordDO::getStatus, status); - } - -} diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/mysql/product/ErpProductUnitMapper.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/mysql/product/ErpProductUnitMapper.java new file mode 100644 index 0000000..4c83f6e --- /dev/null +++ b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/mysql/product/ErpProductUnitMapper.java @@ -0,0 +1,42 @@ +package cn.iocoder.yudao.module.iot.dal.mysql.product; + +import cn.iocoder.yudao.framework.common.pojo.PageResult; +import cn.iocoder.yudao.framework.mybatis.core.mapper.BaseMapperX; + +import cn.iocoder.yudao.framework.mybatis.core.query.LambdaQueryWrapperX; +import cn.iocoder.yudao.module.iot.controller.admin.product.vo.unit.ErpProductUnitPageReqVO; +import cn.iocoder.yudao.module.iot.dal.dataobject.product.ErpProductUnitDO; +import org.apache.ibatis.annotations.Mapper; + +import java.util.List; + +/** + * ERP 产品单位 Mapper + * + * @author 芋道源码 + */ +@Mapper +public interface ErpProductUnitMapper extends BaseMapperX { + + default PageResult selectPage(ErpProductUnitPageReqVO reqVO) { + return selectPage(reqVO, new LambdaQueryWrapperX() + .likeIfPresent(ErpProductUnitDO::getName, reqVO.getName()) + .eqIfPresent(ErpProductUnitDO::getStatus, reqVO.getStatus()) + .orderByDesc(ErpProductUnitDO::getId)); + } + + default PageResult selectProductUnitExist(ErpProductUnitPageReqVO reqVO) { + return selectPage(reqVO, new LambdaQueryWrapperX() + .eqIfPresent(ErpProductUnitDO::getName, reqVO.getName()) + .orderByDesc(ErpProductUnitDO::getId)); + } + + default ErpProductUnitDO selectByName(String name) { + return selectOne(ErpProductUnitDO::getName, name); + } + + default List selectListByStatus(Integer status) { + return selectList(ErpProductUnitDO::getStatus, status); + } + +} \ No newline at end of file diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/mysql/product/IotProductCategoryMapper.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/mysql/product/IotProductCategoryMapper.java deleted file mode 100644 index dc9367b..0000000 --- a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/mysql/product/IotProductCategoryMapper.java +++ /dev/null @@ -1,38 +0,0 @@ -package cn.iocoder.yudao.module.iot.dal.mysql.product; - -import cn.iocoder.yudao.framework.common.pojo.PageResult; -import cn.iocoder.yudao.framework.mybatis.core.mapper.BaseMapperX; -import cn.iocoder.yudao.framework.mybatis.core.query.LambdaQueryWrapperX; -import cn.iocoder.yudao.module.iot.controller.admin.product.vo.category.IotProductCategoryPageReqVO; -import cn.iocoder.yudao.module.iot.dal.dataobject.product.IotProductCategoryDO; -import org.apache.ibatis.annotations.Mapper; - -import javax.annotation.Nullable; -import java.time.LocalDateTime; -import java.util.List; - -/** - * IoT 产品分类 Mapper - * - * @author 芋道源码 - */ -@Mapper -public interface IotProductCategoryMapper extends BaseMapperX { - - default PageResult selectPage(IotProductCategoryPageReqVO reqVO) { - return selectPage(reqVO, new LambdaQueryWrapperX() - .likeIfPresent(IotProductCategoryDO::getName, reqVO.getName()) - .betweenIfPresent(IotProductCategoryDO::getCreateTime, reqVO.getCreateTime()) - .orderByAsc(IotProductCategoryDO::getSort)); - } - - default List selectListByStatus(Integer status) { - return selectList(IotProductCategoryDO::getStatus, status); - } - - default Long selectCountByCreateTime(@Nullable LocalDateTime createTime) { - return selectCount(new LambdaQueryWrapperX() - .geIfPresent(IotProductCategoryDO::getCreateTime, createTime)); - } - -} \ No newline at end of file diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/mysql/product/IotProductMapper.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/mysql/product/IotProductMapper.java deleted file mode 100644 index 8c611d0..0000000 --- a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/mysql/product/IotProductMapper.java +++ /dev/null @@ -1,50 +0,0 @@ -package cn.iocoder.yudao.module.iot.dal.mysql.product; - -import cn.iocoder.yudao.framework.common.pojo.PageResult; -import cn.iocoder.yudao.framework.mybatis.core.mapper.BaseMapperX; -import cn.iocoder.yudao.framework.mybatis.core.query.LambdaQueryWrapperX; -import cn.iocoder.yudao.module.iot.controller.admin.product.vo.product.IotProductPageReqVO; -import cn.iocoder.yudao.module.iot.dal.dataobject.product.IotProductDO; -import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; -import org.apache.ibatis.annotations.Mapper; - -import javax.annotation.Nullable; -import java.time.LocalDateTime; -import java.util.List; - -/** - * IoT 产品 Mapper - * - * @author ahh - */ -@Mapper -public interface IotProductMapper extends BaseMapperX { - - default PageResult selectPage(IotProductPageReqVO reqVO) { - return selectPage(reqVO, new LambdaQueryWrapperX() - .likeIfPresent(IotProductDO::getName, reqVO.getName()) - .likeIfPresent(IotProductDO::getProductKey, reqVO.getProductKey()) - .orderByDesc(IotProductDO::getId)); - } - - default List selectList(Integer deviceType) { - return selectList(new LambdaQueryWrapperX() - .eqIfPresent(IotProductDO::getDeviceType, deviceType) - .orderByDesc(IotProductDO::getId)); - } - - default IotProductDO selectByProductKey(String productKey) { - return selectOne(new LambdaQueryWrapper() - .apply("LOWER(product_key) = {0}", productKey.toLowerCase())); - } - - default List selectListByStatus(Integer status) { - return selectList(IotProductDO::getStatus, status); - } - - default Long selectCountByCreateTime(@Nullable LocalDateTime createTime) { - return selectCount(new LambdaQueryWrapperX() - .geIfPresent(IotProductDO::getCreateTime, createTime)); - } - -} \ No newline at end of file diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/mysql/rule/IotDataRuleMapper.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/mysql/rule/IotDataRuleMapper.java deleted file mode 100644 index ce2eeb0..0000000 --- a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/mysql/rule/IotDataRuleMapper.java +++ /dev/null @@ -1,42 +0,0 @@ -package cn.iocoder.yudao.module.iot.dal.mysql.rule; - -import cn.iocoder.yudao.framework.common.pojo.PageResult; -import cn.iocoder.yudao.framework.mybatis.core.mapper.BaseMapperX; -import cn.iocoder.yudao.framework.mybatis.core.query.LambdaQueryWrapperX; -import cn.iocoder.yudao.framework.mybatis.core.util.MyBatisUtils; -import cn.iocoder.yudao.module.iot.controller.admin.rule.vo.data.rule.IotDataRulePageReqVO; -import cn.iocoder.yudao.module.iot.dal.dataobject.rule.IotDataRuleDO; -import org.apache.ibatis.annotations.Mapper; - -import java.util.List; - -/** - * IoT 数据流转规则 Mapper - * - * @author 芋道源码 - */ -@Mapper -public interface IotDataRuleMapper extends BaseMapperX { - - default PageResult selectPage(IotDataRulePageReqVO reqVO) { - return selectPage(reqVO, new LambdaQueryWrapperX() - .likeIfPresent(IotDataRuleDO::getName, reqVO.getName()) - .eqIfPresent(IotDataRuleDO::getStatus, reqVO.getStatus()) - .betweenIfPresent(IotDataRuleDO::getCreateTime, reqVO.getCreateTime()) - .orderByDesc(IotDataRuleDO::getId)); - } - - default List selectListBySinkId(Long sinkId) { - return selectList(new LambdaQueryWrapperX() - .apply(MyBatisUtils.findInSet("sink_ids", sinkId))); - } - - default List selectListByStatus(Integer status) { - return selectList(IotDataRuleDO::getStatus, status); - } - - default IotDataRuleDO selectByName(String name) { - return selectOne(IotDataRuleDO::getName, name); - } - -} \ No newline at end of file diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/mysql/rule/IotDataSinkMapper.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/mysql/rule/IotDataSinkMapper.java deleted file mode 100644 index 57e2a84..0000000 --- a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/mysql/rule/IotDataSinkMapper.java +++ /dev/null @@ -1,37 +0,0 @@ -package cn.iocoder.yudao.module.iot.dal.mysql.rule; - -import cn.iocoder.yudao.framework.common.pojo.PageResult; -import cn.iocoder.yudao.framework.mybatis.core.mapper.BaseMapperX; -import cn.iocoder.yudao.framework.mybatis.core.query.LambdaQueryWrapperX; -import cn.iocoder.yudao.module.iot.controller.admin.rule.vo.data.sink.IotDataSinkPageReqVO; -import cn.iocoder.yudao.module.iot.dal.dataobject.rule.IotDataSinkDO; -import org.apache.ibatis.annotations.Mapper; - -import java.util.List; - -/** - * IoT 数据流转目的 Mapper - * - * @author HUIHUI - */ -@Mapper -public interface IotDataSinkMapper extends BaseMapperX { - - default PageResult selectPage(IotDataSinkPageReqVO reqVO) { - return selectPage(reqVO, new LambdaQueryWrapperX() - .likeIfPresent(IotDataSinkDO::getName, reqVO.getName()) - .eqIfPresent(IotDataSinkDO::getStatus, reqVO.getStatus()) - .eqIfPresent(IotDataSinkDO::getType, reqVO.getType()) - .betweenIfPresent(IotDataSinkDO::getCreateTime, reqVO.getCreateTime()) - .orderByDesc(IotDataSinkDO::getId)); - } - - default List selectListByStatus(Integer status) { - return selectList(IotDataSinkDO::getStatus, status); - } - - default IotDataSinkDO selectByName(String name) { - return selectOne(IotDataSinkDO::getName, name); - } - -} \ No newline at end of file diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/mysql/rule/IotSceneRuleMapper.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/mysql/rule/IotSceneRuleMapper.java deleted file mode 100644 index 4fd6490..0000000 --- a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/mysql/rule/IotSceneRuleMapper.java +++ /dev/null @@ -1,33 +0,0 @@ -package cn.iocoder.yudao.module.iot.dal.mysql.rule; - -import cn.iocoder.yudao.framework.common.pojo.PageResult; -import cn.iocoder.yudao.framework.mybatis.core.mapper.BaseMapperX; -import cn.iocoder.yudao.framework.mybatis.core.query.LambdaQueryWrapperX; -import cn.iocoder.yudao.module.iot.controller.admin.rule.vo.scene.IotSceneRulePageReqVO; -import cn.iocoder.yudao.module.iot.dal.dataobject.rule.IotSceneRuleDO; -import org.apache.ibatis.annotations.Mapper; - -import java.util.List; - -/** - * IoT 场景联动 Mapper - * - * @author HUIHUI - */ -@Mapper -public interface IotSceneRuleMapper extends BaseMapperX { - - default PageResult selectPage(IotSceneRulePageReqVO reqVO) { - return selectPage(reqVO, new LambdaQueryWrapperX() - .likeIfPresent(IotSceneRuleDO::getName, reqVO.getName()) - .likeIfPresent(IotSceneRuleDO::getDescription, reqVO.getDescription()) - .eqIfPresent(IotSceneRuleDO::getStatus, reqVO.getStatus()) - .betweenIfPresent(IotSceneRuleDO::getCreateTime, reqVO.getCreateTime()) - .orderByDesc(IotSceneRuleDO::getId)); - } - - default List selectListByStatus(Integer status) { - return selectList(IotSceneRuleDO::getStatus, status); - } - -} diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/mysql/thingmodel/IotThingModelMapper.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/mysql/thingmodel/IotThingModelMapper.java deleted file mode 100644 index 64529df..0000000 --- a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/mysql/thingmodel/IotThingModelMapper.java +++ /dev/null @@ -1,65 +0,0 @@ -package cn.iocoder.yudao.module.iot.dal.mysql.thingmodel; - -import cn.iocoder.yudao.framework.common.pojo.PageResult; -import cn.iocoder.yudao.framework.mybatis.core.mapper.BaseMapperX; -import cn.iocoder.yudao.framework.mybatis.core.query.LambdaQueryWrapperX; -import cn.iocoder.yudao.module.iot.controller.admin.thingmodel.vo.IotThingModelListReqVO; -import cn.iocoder.yudao.module.iot.controller.admin.thingmodel.vo.IotThingModelPageReqVO; -import cn.iocoder.yudao.module.iot.dal.dataobject.thingmodel.IotThingModelDO; -import org.apache.ibatis.annotations.Mapper; - -import java.util.Collection; -import java.util.List; - -/** - * IoT 产品物模型 Mapper - * - * @author 芋道源码 - */ -@Mapper -public interface IotThingModelMapper extends BaseMapperX { - - default PageResult selectPage(IotThingModelPageReqVO reqVO) { - return selectPage(reqVO, new LambdaQueryWrapperX() - .eqIfPresent(IotThingModelDO::getIdentifier, reqVO.getIdentifier()) - .likeIfPresent(IotThingModelDO::getName, reqVO.getName()) - .eqIfPresent(IotThingModelDO::getType, reqVO.getType()) - .eqIfPresent(IotThingModelDO::getProductId, reqVO.getProductId()) - .orderByDesc(IotThingModelDO::getId)); - } - - default List selectList(IotThingModelListReqVO reqVO) { - return selectList(new LambdaQueryWrapperX() - .eqIfPresent(IotThingModelDO::getIdentifier, reqVO.getIdentifier()) - .likeIfPresent(IotThingModelDO::getName, reqVO.getName()) - .eqIfPresent(IotThingModelDO::getType, reqVO.getType()) - .eqIfPresent(IotThingModelDO::getProductId, reqVO.getProductId()) - .orderByDesc(IotThingModelDO::getId)); - } - - default IotThingModelDO selectByProductIdAndIdentifier(Long productId, String identifier) { - return selectOne(IotThingModelDO::getProductId, productId, - IotThingModelDO::getIdentifier, identifier); - } - - default List selectListByProductId(Long productId) { - return selectList(IotThingModelDO::getProductId, productId); - } - - default List selectListByProductIdAndIdentifiers(Long productId, Collection identifiers) { - return selectList(new LambdaQueryWrapperX() - .eq(IotThingModelDO::getProductId, productId) - .in(IotThingModelDO::getIdentifier, identifiers)); - } - - default List selectListByProductIdAndType(Long productId, Integer type) { - return selectList(IotThingModelDO::getProductId, productId, - IotThingModelDO::getType, type); - } - - default IotThingModelDO selectByProductIdAndName(Long productId, String name) { - return selectOne(IotThingModelDO::getProductId, productId, - IotThingModelDO::getName, name); - } - -} \ No newline at end of file diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/redis/RedisKeyConstants.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/redis/RedisKeyConstants.java deleted file mode 100644 index 95d2102..0000000 --- a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/redis/RedisKeyConstants.java +++ /dev/null @@ -1,95 +0,0 @@ -package cn.iocoder.yudao.module.iot.dal.redis; - -import cn.iocoder.yudao.module.iot.dal.dataobject.device.IotDevicePropertyDO; - -/** - * IoT Redis Key 枚举类 - * - * @author 芋道源码 - */ -public interface RedisKeyConstants { - - /** - * 设备属性的数据缓存,采用 HASH 结构 - *

- * KEY 格式:device_property:{deviceId} - * HASH KEY:identifier 属性标识 - * VALUE 数据类型:String(JSON) {@link IotDevicePropertyDO} - */ - String DEVICE_PROPERTY = "iot:device_property:%d"; - - /** - * 设备的最后上报时间,采用 ZSET 结构 - * - * KEY 格式:{deviceId} - * SCORE:上报时间 - */ - String DEVICE_REPORT_TIMES = "iot:device_report_times"; - - /** - * 设备关联的网关 serverId 缓存,采用 HASH 结构 - * - * KEY 格式:device_server_id - * HASH KEY:{deviceId} - * VALUE 数据类型:String serverId - */ - String DEVICE_SERVER_ID = "iot:device_server_id"; - - /** - * 设备信息的数据缓存,使用 Spring Cache 操作(忽略租户) - * - * KEY 格式 1:device_${deviceId} - * KEY 格式 2:device_${productKey}_${deviceName} - * VALUE 数据类型:String(JSON) - */ - String DEVICE = "iot:device"; - - /** - * 产品信息的数据缓存,使用 Spring Cache 操作(忽略租户) - * - * KEY 格式:product_${productId} - * VALUE 数据类型:String(JSON) - */ - String PRODUCT = "iot:product"; - - /** - * 物模型的数据缓存,使用 Spring Cache 操作(忽略租户) - * - * KEY 格式:thing_model_${productId} - * VALUE 数据类型:String 数组(JSON),即 {@link cn.iocoder.yudao.module.iot.dal.dataobject.thingmodel.IotThingModelDO} 列表 - */ - String THING_MODEL_LIST = "iot:thing_model_list"; - - /** - * 数据流转规则的数据缓存,使用 Spring Cache 操作 - * - * KEY 格式:data_rule_list_${deviceId}_${method}_${identifier} - * VALUE 数据类型:String 数组(JSON),即 {@link cn.iocoder.yudao.module.iot.dal.dataobject.rule.IotDataRuleDO} 列表 - */ - String DATA_RULE_LIST = "iot:data_rule_list"; - - /** - * 数据目的的数据缓存,使用 Spring Cache 操作 - * - * KEY 格式:data_sink_${id} - * VALUE 数据类型:String(JSON),即 {@link cn.iocoder.yudao.module.iot.dal.dataobject.rule.IotDataSinkDO} - */ - String DATA_SINK = "iot:data_sink"; - - /** - * 场景联动规则的数据缓存,使用 Spring Cache 操作 - *

- * KEY 格式:scene_rule_list_${productId}_${deviceId} - * VALUE 数据类型:String 数组(JSON),即 {@link cn.iocoder.yudao.module.iot.dal.dataobject.rule.IotSceneRuleDO} 列表 - */ - String SCENE_RULE_LIST = "iot:scene_rule_list"; - - /** - * WebSocket 连接分布式锁 - *

- * KEY 格式:websocket_connect_lock:${serverUrl} - * 用于保证 WebSocket 重连操作的线程安全 - */ - String WEBSOCKET_CONNECT_LOCK = "iot:websocket_connect_lock:%s"; - -} diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/redis/device/DevicePropertyRedisDAO.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/redis/device/DevicePropertyRedisDAO.java deleted file mode 100644 index 5b627aa..0000000 --- a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/redis/device/DevicePropertyRedisDAO.java +++ /dev/null @@ -1,50 +0,0 @@ -package cn.iocoder.yudao.module.iot.dal.redis.device; - -import cn.hutool.core.collection.CollUtil; -import cn.iocoder.yudao.framework.common.util.json.JsonUtils; -import cn.iocoder.yudao.module.iot.dal.dataobject.device.IotDevicePropertyDO; -import org.springframework.data.redis.core.StringRedisTemplate; -import org.springframework.stereotype.Repository; - -import javax.annotation.Resource; -import java.util.Collections; -import java.util.Map; - -import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertMap; -import static cn.iocoder.yudao.module.iot.dal.redis.RedisKeyConstants.DEVICE_PROPERTY; - -/** - * {@link IotDevicePropertyDO} 的 Redis DAO - */ -@Repository -public class DevicePropertyRedisDAO { - - @Resource - private StringRedisTemplate stringRedisTemplate; - - public Map get(Long id) { - String redisKey = formatKey(id); - Map entries = stringRedisTemplate.opsForHash().entries(redisKey); - if (CollUtil.isEmpty(entries)) { - return Collections.emptyMap(); - } - return convertMap(entries.entrySet(), - entry -> (String) entry.getKey(), - entry -> JsonUtils.parseObject((String) entry.getValue(), IotDevicePropertyDO.class)); - } - - public void putAll(Long id, Map properties) { - if (CollUtil.isEmpty(properties)) { - return; - } - String redisKey = formatKey(id); - stringRedisTemplate.opsForHash().putAll(redisKey, convertMap(properties.entrySet(), - Map.Entry::getKey, - entry -> JsonUtils.toJsonString(entry.getValue()))); - } - - private static String formatKey(Long id) { - return String.format(DEVICE_PROPERTY, id); - } - -} diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/redis/device/DeviceReportTimeRedisDAO.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/redis/device/DeviceReportTimeRedisDAO.java deleted file mode 100644 index 7cff13e..0000000 --- a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/redis/device/DeviceReportTimeRedisDAO.java +++ /dev/null @@ -1,36 +0,0 @@ -package cn.iocoder.yudao.module.iot.dal.redis.device; - -import cn.hutool.core.date.LocalDateTimeUtil; -import cn.iocoder.yudao.module.iot.dal.redis.RedisKeyConstants; -import org.springframework.data.redis.core.StringRedisTemplate; -import org.springframework.stereotype.Repository; - -import javax.annotation.Resource; -import java.time.LocalDateTime; -import java.util.Set; - -import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertSet; - -/** - * 设备的最后上报时间的 Redis DAO - * - * @author 芋道源码 - */ -@Repository -public class DeviceReportTimeRedisDAO { - - @Resource - private StringRedisTemplate stringRedisTemplate; - - public void update(Long deviceId, LocalDateTime reportTime) { - stringRedisTemplate.opsForZSet().add(RedisKeyConstants.DEVICE_REPORT_TIMES, String.valueOf(deviceId), - LocalDateTimeUtil.toEpochMilli(reportTime)); - } - - public Set range(LocalDateTime maxReportTime) { - Set values = stringRedisTemplate.opsForZSet().rangeByScore(RedisKeyConstants.DEVICE_REPORT_TIMES, - 0, LocalDateTimeUtil.toEpochMilli(maxReportTime)); - return convertSet(values, Long::parseLong); - } - -} diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/redis/device/DeviceServerIdRedisDAO.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/redis/device/DeviceServerIdRedisDAO.java deleted file mode 100644 index 864ba80..0000000 --- a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/redis/device/DeviceServerIdRedisDAO.java +++ /dev/null @@ -1,31 +0,0 @@ -package cn.iocoder.yudao.module.iot.dal.redis.device; - -import cn.iocoder.yudao.module.iot.dal.redis.RedisKeyConstants; -import org.springframework.data.redis.core.StringRedisTemplate; -import org.springframework.stereotype.Repository; - -import javax.annotation.Resource; - -/** - * 设备关联的网关 serverId 的 Redis DAO - * - * @author 芋道源码 - */ -@Repository -public class DeviceServerIdRedisDAO { - - @Resource - private StringRedisTemplate stringRedisTemplate; - - public void update(Long deviceId, String serverId) { - stringRedisTemplate.opsForHash().put(RedisKeyConstants.DEVICE_SERVER_ID, - String.valueOf(deviceId), serverId); - } - - public String get(Long deviceId) { - Object value = stringRedisTemplate.opsForHash().get(RedisKeyConstants.DEVICE_SERVER_ID, - String.valueOf(deviceId)); - return value != null ? (String) value : null; - } - -} \ No newline at end of file diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/tdengine/IotDeviceMessageMapper.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/tdengine/IotDeviceMessageMapper.java index b09895f..44e9a84 100644 --- a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/tdengine/IotDeviceMessageMapper.java +++ b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/tdengine/IotDeviceMessageMapper.java @@ -1,79 +1,79 @@ -package cn.iocoder.yudao.module.iot.dal.tdengine; - -import cn.iocoder.yudao.module.iot.controller.admin.device.vo.message.IotDeviceMessagePageReqVO; -import cn.iocoder.yudao.module.iot.dal.dataobject.device.IotDeviceMessageDO; -import cn.iocoder.yudao.module.iot.framework.tdengine.core.annotation.TDengineDS; -import com.baomidou.mybatisplus.annotation.InterceptorIgnore; -import com.baomidou.mybatisplus.core.metadata.IPage; -import org.apache.ibatis.annotations.Mapper; -import org.apache.ibatis.annotations.Param; - -import java.util.Collection; -import java.util.List; -import java.util.Map; - -/** - * 设备消息 {@link IotDeviceMessageDO} Mapper 接口 - */ -@Mapper -@TDengineDS -@InterceptorIgnore(tenantLine = "true") // 避免 SQL 解析,因为 JSqlParser 对 TDengine 的 SQL 解析会报错 -public interface IotDeviceMessageMapper { - - /** - * 创建设备消息超级表 - */ - void createSTable(); - - /** - * 查询设备消息表是否存在 - * - * @return 存在则返回表名;不存在则返回 null - */ - String showSTable(); - - /** - * 插入设备消息数据 - * - * 如果子表不存在,会自动创建子表 - * - * @param message 设备消息数据 - */ - void insert(IotDeviceMessageDO message); - - /** - * 获得设备消息分页 - * - * @param reqVO 分页查询条件 - * @return 设备消息列表 - */ - IPage selectPage(IPage page, - @Param("reqVO") IotDeviceMessagePageReqVO reqVO); - - /** - * 统计设备消息数量 - * - * @param createTime 创建时间,如果为空,则统计所有消息数量 - * @return 消息数量 - */ - Long selectCountByCreateTime(@Param("createTime") Long createTime); - - /** - * 按照 requestIds 批量查询消息 - * - * @param deviceId 设备编号 - * @param requestIds 请求编号集合 - * @param reply 是否回复消息 - * @return 消息列表 - */ - List selectListByRequestIdsAndReply(@Param("deviceId") Long deviceId, - @Param("requestIds") Collection requestIds, - @Param("reply") Boolean reply); - - /** - * 按照时间范围(小时),统计设备的消息数量 - */ - List> selectDeviceMessageCountGroupByDate(@Param("startTime") Long startTime, - @Param("endTime") Long endTime); - -} +//package cn.iocoder.yudao.module.iot.dal.tdengine; +// +//import cn.iocoder.yudao.module.iot.controller.admin.device.vo.message.IotDeviceMessagePageReqVO; +//import cn.iocoder.yudao.module.iot.dal.dataobject.device.IotDeviceMessageDO; +//import cn.iocoder.yudao.module.iot.framework.tdengine.core.annotation.TDengineDS; +//import com.baomidou.mybatisplus.annotation.InterceptorIgnore; +//import com.baomidou.mybatisplus.core.metadata.IPage; +//import org.apache.ibatis.annotations.Mapper; +//import org.apache.ibatis.annotations.Param; +// +//import java.util.Collection; +//import java.util.List; +//import java.util.Map; +// +///** +// * 设备消息 {@link IotDeviceMessageDO} Mapper 接口 +// */ +//@Mapper +//@TDengineDS +//@InterceptorIgnore(tenantLine = "true") // 避免 SQL 解析,因为 JSqlParser 对 TDengine 的 SQL 解析会报错 +//public interface IotDeviceMessageMapper { +// +// /** +// * 创建设备消息超级表 +// */ +// void createSTable(); +// +// /** +// * 查询设备消息表是否存在 +// * +// * @return 存在则返回表名;不存在则返回 null +// */ +// String showSTable(); +// +// /** +// * 插入设备消息数据 +// * +// * 如果子表不存在,会自动创建子表 +// * +// * @param message 设备消息数据 +// */ +// void insert(IotDeviceMessageDO message); +// +// /** +// * 获得设备消息分页 +// * +// * @param reqVO 分页查询条件 +// * @return 设备消息列表 +// */ +// IPage selectPage(IPage page, +// @Param("reqVO") IotDeviceMessagePageReqVO reqVO); +// +// /** +// * 统计设备消息数量 +// * +// * @param createTime 创建时间,如果为空,则统计所有消息数量 +// * @return 消息数量 +// */ +// Long selectCountByCreateTime(@Param("createTime") Long createTime); +// +// /** +// * 按照 requestIds 批量查询消息 +// * +// * @param deviceId 设备编号 +// * @param requestIds 请求编号集合 +// * @param reply 是否回复消息 +// * @return 消息列表 +// */ +// List selectListByRequestIdsAndReply(@Param("deviceId") Long deviceId, +// @Param("requestIds") Collection requestIds, +// @Param("reply") Boolean reply); +// +// /** +// * 按照时间范围(小时),统计设备的消息数量 +// */ +// List> selectDeviceMessageCountGroupByDate(@Param("startTime") Long startTime, +// @Param("endTime") Long endTime); +// +//} diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/tdengine/IotDevicePropertyMapper.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/tdengine/IotDevicePropertyMapper.java index a43dcd7..484589d 100644 --- a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/tdengine/IotDevicePropertyMapper.java +++ b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/tdengine/IotDevicePropertyMapper.java @@ -3,9 +3,7 @@ package cn.iocoder.yudao.module.iot.dal.tdengine; import cn.hutool.core.collection.CollUtil; import cn.hutool.core.util.ObjectUtil; import cn.hutool.core.util.StrUtil; -import cn.iocoder.yudao.module.iot.controller.admin.device.vo.property.IotDevicePropertyHistoryListReqVO; -import cn.iocoder.yudao.module.iot.controller.admin.device.vo.property.IotDevicePropertyRespVO; -import cn.iocoder.yudao.module.iot.dal.dataobject.device.IotDeviceDO; + import cn.iocoder.yudao.module.iot.framework.tdengine.core.TDengineTableField; import cn.iocoder.yudao.module.iot.framework.tdengine.core.annotation.TDengineDS; import com.baomidou.mybatisplus.annotation.InterceptorIgnore; @@ -79,10 +77,5 @@ public interface IotDevicePropertyMapper { void alterProductPropertySTableDropField(@Param("productId") Long productId, @Param("field") TDengineTableField field); - void insert(@Param("device") IotDeviceDO device, - @Param("properties") Map properties, - @Param("reportTime") Long reportTime); - - List selectListByHistory(@Param("reqVO") IotDevicePropertyHistoryListReqVO reqVO); } diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/enums/ErrorCodeConstants.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/enums/ErrorCodeConstants.java index 065eb2d..590b9c6 100644 --- a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/enums/ErrorCodeConstants.java +++ b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/enums/ErrorCodeConstants.java @@ -27,6 +27,7 @@ public interface ErrorCodeConstants { ErrorCode DEVICE_NOT_EXISTS = new ErrorCode(1_050_003_000, "设备不存在"); ErrorCode DEVICE_NAME_EXISTS = new ErrorCode(1_050_003_001, "设备名称在同一产品下必须唯一"); ErrorCode DEVICE_GATEWAY_HAS_SUB = new ErrorCode(1_050_003_002, "网关设备存在已绑定的子设备,不允许删除"); + ErrorCode GATEWAY_NOT_EXISTS = new ErrorCode(1_003_000_000, "网关不存在"); ErrorCode DEVICE_KEY_EXISTS = new ErrorCode(1_050_003_003, "设备标识已经存在"); ErrorCode DEVICE_GATEWAY_NOT_EXISTS = new ErrorCode(1_050_003_004, "网关设备不存在"); ErrorCode DEVICE_NOT_GATEWAY = new ErrorCode(1_050_003_005, "设备不是网关设备"); @@ -35,6 +36,29 @@ public interface ErrorCodeConstants { ErrorCode DEVICE_SERIAL_NUMBER_EXISTS = new ErrorCode(1_050_003_008, "设备序列号已存在,序列号必须全局唯一"); ErrorCode DEVICE_NOT_GATEWAY_SUB = new ErrorCode(1_050_003_009, "设备【{}/{}】不是网关子设备类型,无法绑定到网关"); ErrorCode DEVICE_GATEWAY_BINDTO_EXISTS = new ErrorCode(1_050_003_010, "设备【{}/{}】已绑定到其他网关,请先解绑"); + ErrorCode DEVICE_CONTACT_MODEL_NOT_EXISTS = new ErrorCode(1_050_003_011, "查询不到该点位"); + ErrorCode DEVICE_MODEL_POINT_CODE_EXISTS = new ErrorCode(1_003_000_005, "采集设备采集点位编码已存在"); + ErrorCode DEVICE_MODEL_ATTRIBUTE_POTIN_CODE_EXISTS = new ErrorCode(1_003_000_005, "采集设备模型点位编码已存在"); + ErrorCode DEVICE_MODEL_ATTRIBUTE_NOT_EXISTS = new ErrorCode(1_003_000_005, "采集设备模型点位不存在"); + ErrorCode DEVICE_ID_DOES_NOT_EXIST= new ErrorCode(1_003_000_011, "该设备ID不能为空"); + ErrorCode POINT_ID_MODEL_NOT_EXISTS = new ErrorCode(1_003_000_004, "该点位参数ID不能为空"); + ErrorCode DEVICE_MODEL_NOT_EXISTS = new ErrorCode(1_003_000_001, "采集设备模型不存在"); + ErrorCode DEVICE_CODE_EXISTS = new ErrorCode(1_003_000_000, "采集点编码已存在"); + ErrorCode DEVICE_ATTRIBUTE_TYPE_NOT_EXISTS = new ErrorCode(1_003_000_007, "采集点分类不存在"); + ErrorCode DEVICE_ATTRIBUTE_TYPE_REFERENCES_EXISTS = new ErrorCode(1_003_000_008, "存在采集点类型已被引用,请先删除对应引用"); + ErrorCode DEVICE_MODEL_RULES_NOT_EXISTS = new ErrorCode(1_004_000_002, "点位规则关联模型不存在"); + ErrorCode DEVICE_OPERATION_RECORD_NOT_EXISTS = new ErrorCode(1_004_000_003, "运行记录不存在"); + ErrorCode DEVICE_POINT_RULES_NOT_EXISTS = new ErrorCode(1_004_000_001, "点位规则不存在"); + ErrorCode DEVICE_WARINNING_RECORD_NOT_EXISTS = new ErrorCode(1_004_000_004, "告警记录不存在"); + ErrorCode DEVICE_MQTT_TOPIC_ALREADY_EXIST = new ErrorCode(1_003_000_000, "该MQTT主题已被其他设备订阅,一台设备只能绑定一个主题。"); + ErrorCode DEVICE_ATTRIBUTE_NOT_EXISTS = new ErrorCode(1_003_000_006, "设备属性不存在"); + ErrorCode DEVICE_REFERENCES_EXIST = new ErrorCode(1_003_000_000, "存在设备已被引用,请先删除引用。"); + ErrorCode DEVICE_CODE_ALREADY_EXISTS = new ErrorCode(1_003_000_002, "采集设备编码已存在"); + ErrorCode DEVICE_ID_MODEL_NOT_EXISTS = new ErrorCode(1_003_000_003, "该设备模型ID不能为空"); + ErrorCode DEVICE_DOES_NOT_EXIST= new ErrorCode(1_003_000_010, "该采集设备不存在"); + ErrorCode DEVICE_MQTT_TOPIC_EXIST = new ErrorCode(1_003_000_000, "设备MQTT主题不存在。"); + + // 拓扑管理相关错误码 1-050-003-100 ErrorCode DEVICE_TOPO_PARAMS_INVALID = new ErrorCode(1_050_003_100, "拓扑管理参数无效"); ErrorCode DEVICE_TOPO_SUB_DEVICE_USERNAME_INVALID = new ErrorCode(1_050_003_101, "子设备用户名格式无效"); @@ -102,4 +126,18 @@ public interface ErrorCodeConstants { // ========== IoT 告警记录 1-050-014-000 ========== ErrorCode ALERT_RECORD_NOT_EXISTS = new ErrorCode(1_050_014_000, "IoT 告警记录不存在"); + // ======================================= Tdengine ============================================ + ErrorCode TABLE_CREATION_FAILED = new ErrorCode(1_004_000_008, "TDengine 表创建失败"); + ErrorCode COLOUMN_CREATION_FAILED = new ErrorCode(1_004_000_008, "TDengine 列创建失败"); + ErrorCode COLUMN_RENAME_FAILED = new ErrorCode(1_004_000_008, "列名修改失败"); + + + // ========== ERP 产品单位 1-030-502-000 ========== + ErrorCode PRODUCT_UNIT_NOT_EXISTS = new ErrorCode(1_030_502_000, "产品单位不存在"); + ErrorCode PRODUCT_UNIT_NAME_DUPLICATE = new ErrorCode(1_030_502_001, "已存在该名字的产品单位"); + ErrorCode PRODUCT_UNIT_EXITS_PRODUCT = new ErrorCode(1_030_502_002, "存在产品使用该单位,无法删除"); + + // ========== 客户组织节点表 1_030_602_000 ========== + ErrorCode ORG_NODE_NOT_EXISTS = new ErrorCode(1_030_602_000 , "客户组织节点表,用于管理客户、车间、产线等多级组织结构不存在"); + } \ No newline at end of file diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/enums/product/ProductUnitEnum.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/enums/product/ProductUnitEnum.java new file mode 100644 index 0000000..e9d2020 --- /dev/null +++ b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/enums/product/ProductUnitEnum.java @@ -0,0 +1,28 @@ +package cn.iocoder.yudao.module.iot.enums.product; + +import lombok.AllArgsConstructor; +import lombok.Getter; + +import java.util.Arrays; + +//关键重量单位不允许更改 +@Getter +@AllArgsConstructor +public enum ProductUnitEnum { + + Ton(2L, "t"), + Kilogram(3L, "kg"), + Gram(4L, "g"), + Each(5L, "个"); + + /** + * 值 + */ + private final Long unitId; + /** + * 名 + */ + private final String name; + + +} \ No newline at end of file diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/framework/constant/AddressDataType.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/framework/constant/AddressDataType.java new file mode 100644 index 0000000..feb73c7 --- /dev/null +++ b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/framework/constant/AddressDataType.java @@ -0,0 +1,114 @@ +package cn.iocoder.yudao.module.iot.framework.constant; + +import lombok.AllArgsConstructor; +import lombok.Data; + +import java.util.HashMap; +import java.util.Map; + +@Data +@AllArgsConstructor +public class AddressDataType { + private static Map tdDataTypeMap = new HashMap<>(); + private static Map mysqlDataTypeMap = new HashMap<>(); + private static Map gatewayDataTypeMap = new HashMap<>(); + + static { + tdDataTypeMap.put("bool", "BOOL"); + tdDataTypeMap.put("int8", "INT"); + tdDataTypeMap.put("int16", "INT"); + tdDataTypeMap.put("int32", "INT"); + tdDataTypeMap.put("int64", "BIGINT"); + tdDataTypeMap.put("uint8", "INT"); + tdDataTypeMap.put("uint16", "INT"); + tdDataTypeMap.put("uint32", "BIGINT"); + tdDataTypeMap.put("uint64", "BIGINT"); + tdDataTypeMap.put("float32", "float"); + tdDataTypeMap.put("float64", "float"); + tdDataTypeMap.put("string", "NCHAR(255)"); + tdDataTypeMap.put("date", "TIMESTAMP"); + tdDataTypeMap.put("bcd", "BINARY(64)"); + + gatewayDataTypeMap.put("bool", 1); + gatewayDataTypeMap.put("int8", 2); + gatewayDataTypeMap.put("uint8", 3); + gatewayDataTypeMap.put("int16", 4); + gatewayDataTypeMap.put("uint16", 5); + gatewayDataTypeMap.put("int32", 6); + gatewayDataTypeMap.put("uint32", 7); + gatewayDataTypeMap.put("int64", 8); + gatewayDataTypeMap.put("uint64", 9); + gatewayDataTypeMap.put("float32", 10); + gatewayDataTypeMap.put("float64", 11); + gatewayDataTypeMap.put("string", 12); + gatewayDataTypeMap.put("date", 13); + gatewayDataTypeMap.put("bcd", 14); + + gatewayDataTypeMap.put("Bool", 1); + gatewayDataTypeMap.put("Int8", 2); + gatewayDataTypeMap.put("Uint8", 3); + gatewayDataTypeMap.put("Int16", 4); + gatewayDataTypeMap.put("Uint16", 5); + gatewayDataTypeMap.put("Int32", 6); + gatewayDataTypeMap.put("Uint32", 7); + gatewayDataTypeMap.put("Int64", 8); + gatewayDataTypeMap.put("Uint64", 9); + gatewayDataTypeMap.put("Float32", 10); + gatewayDataTypeMap.put("Float64", 11); + gatewayDataTypeMap.put("String", 12); + gatewayDataTypeMap.put("Date", 13); + gatewayDataTypeMap.put("Bcd", 14); + + gatewayDataTypeMap.put("BOOL", 1); + gatewayDataTypeMap.put("INT8", 2); + gatewayDataTypeMap.put("UINT8", 3); + gatewayDataTypeMap.put("INT16", 4); + gatewayDataTypeMap.put("UINT16", 5); + gatewayDataTypeMap.put("INT32", 6); + gatewayDataTypeMap.put("UINT32", 7); + gatewayDataTypeMap.put("INT64", 8); + gatewayDataTypeMap.put("UINT64", 9); + gatewayDataTypeMap.put("FLOAT32", 10); + gatewayDataTypeMap.put("FLOAT64", 11); + gatewayDataTypeMap.put("STRING", 12); + gatewayDataTypeMap.put("DATE", 13); + gatewayDataTypeMap.put("BCD", 14); + + mysqlDataTypeMap.put("bool", "BOOL"); + mysqlDataTypeMap.put("int8", "INT"); + mysqlDataTypeMap.put("int16", "INT"); + mysqlDataTypeMap.put("int32", "INT"); + mysqlDataTypeMap.put("int64", "BIGINT"); + mysqlDataTypeMap.put("uint8", "INT"); + mysqlDataTypeMap.put("uint16", "INT"); + mysqlDataTypeMap.put("uint32", "BIGINT"); + mysqlDataTypeMap.put("uint64", "BIGINT"); + mysqlDataTypeMap.put("float32", "float"); + mysqlDataTypeMap.put("float64", "float"); + mysqlDataTypeMap.put("string", "varchar(255)"); + mysqlDataTypeMap.put("date", "TIMESTAMP"); + mysqlDataTypeMap.put("bcd", "double"); + } + + public static Map getDataTypeMap() { + return tdDataTypeMap; + } + + public static String getTdDataType(String originDataType) { + return tdDataTypeMap.get(originDataType); + } + + public static String getTMysqlDataType(String originDataType) { + //return mysqlDataTypeMap.get(originDataType); + return "double(8,4)"; + } + + public static Map getGatewayDataTypeMap() { + return gatewayDataTypeMap; + } + + public static Integer getGatewayDataTypeMap(String originDataType) { + return gatewayDataTypeMap.get(originDataType); + } + +} diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/framework/constant/Constants.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/framework/constant/Constants.java new file mode 100644 index 0000000..b213aaf --- /dev/null +++ b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/framework/constant/Constants.java @@ -0,0 +1,17 @@ +package cn.iocoder.yudao.module.iot.framework.constant; + +public class Constants { + //mqtt时间默认格式 + public final static String MQTT_timestamp_format = "yyyy-MM-dd HH:mm:ss.SSS"; + + //iot设备默认离线时长,秒;距离上次超过这个时间 + public final static int DEVICE_OFFLINE_SECOND = 30; + + /** + * 默认逻辑表达式的变量名称 + */ + public final static String DefaultFormulaVar = "data"; + + + +} diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/framework/constant/DeviceStatusEnum.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/framework/constant/DeviceStatusEnum.java new file mode 100644 index 0000000..bc63928 --- /dev/null +++ b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/framework/constant/DeviceStatusEnum.java @@ -0,0 +1,26 @@ +package cn.iocoder.yudao.module.iot.framework.constant; + +import lombok.AllArgsConstructor; +import lombok.Getter; + +@Getter +@AllArgsConstructor +public enum DeviceStatusEnum { + + 不可用("3"), + 离线("2"), + 在线("1"); + + private final String value; + + // 一个可选的方法,用于根据整数值获取对应的枚举实例 + public static DeviceStatusEnum fromValue(String value) { + for (DeviceStatusEnum status : DeviceStatusEnum.values()) { + if (status.getValue().equals(value)) { + return status; + } + } + throw new IllegalArgumentException("Unknown value: " + value); + } + +} \ No newline at end of file diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/framework/constant/DeviceTypeEnum.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/framework/constant/DeviceTypeEnum.java new file mode 100644 index 0000000..170efd1 --- /dev/null +++ b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/framework/constant/DeviceTypeEnum.java @@ -0,0 +1,24 @@ +package cn.iocoder.yudao.module.iot.framework.constant; + +import cn.hutool.core.util.ObjUtil; +import lombok.AllArgsConstructor; +import lombok.Getter; + +@Getter +@AllArgsConstructor +public enum DeviceTypeEnum { + + modbus("modbus", "modbus"), + siemens("siemens", "siemens"); + private final String value; + + private final String name; + + public static boolean isSiemens(String value) { + return ObjUtil.equal(siemens.value, value); + } + + public static boolean isModbus(String value) { + return ObjUtil.equal(modbus.value, value); + } +} \ No newline at end of file diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/framework/constant/IsEnableConstant.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/framework/constant/IsEnableConstant.java new file mode 100644 index 0000000..46afc04 --- /dev/null +++ b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/framework/constant/IsEnableConstant.java @@ -0,0 +1,12 @@ +package cn.iocoder.yudao.module.iot.framework.constant; + +public class IsEnableConstant { + /** + * 停用 + */ + public final static boolean IsEnableFalse = false; + /** + * 启用 + */ + public final static boolean IsEnableTrue = true; +} diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/framework/mqtt/annotation/Topic.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/framework/mqtt/annotation/Topic.java new file mode 100644 index 0000000..43ab11d --- /dev/null +++ b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/framework/mqtt/annotation/Topic.java @@ -0,0 +1,46 @@ +package cn.iocoder.yudao.module.iot.framework.mqtt.annotation; + +import cn.iocoder.yudao.module.iot.framework.mqtt.entity.Pattern; +import org.springframework.stereotype.Component; + +import java.lang.annotation.*; + +/** + * 自定义标记注解 + * + * @author jie + */ +@Component +@Documented +@Target({ElementType.TYPE}) +@Retention(RetentionPolicy.RUNTIME) +public @interface Topic { + + /** + * topic + * + * @return + */ + String topic() default ""; + + /** + * qos + * + * @return + */ + int qos() default 0; + + /** + * 订阅模式 + * + * @return + */ + Pattern patten() default Pattern.NONE; + + /** + * 共享订阅组 + * + * @return + */ + String group() default "group1"; +} diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/framework/mqtt/common/MqttCallbackImpl.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/framework/mqtt/common/MqttCallbackImpl.java new file mode 100644 index 0000000..c121fbb --- /dev/null +++ b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/framework/mqtt/common/MqttCallbackImpl.java @@ -0,0 +1,103 @@ +package cn.iocoder.yudao.module.iot.framework.mqtt.common; + +import cn.iocoder.yudao.module.iot.framework.mqtt.entity.Pattern; +import cn.iocoder.yudao.module.iot.framework.mqtt.entity.SubscriptTopic; +import lombok.Data; +import lombok.SneakyThrows; +import lombok.extern.slf4j.Slf4j; +import org.eclipse.paho.client.mqttv3.*; + +import java.util.List; + +/** + * mqtt回调类 + * + * @author jie + */ +@Data +@Slf4j +public class MqttCallbackImpl implements MqttCallbackExtended { + private final List topicMap; + private final MqttClient client; + private final MqttConnectOptions option; + + /** + * 客户端断开后触发 + * + * @param throwable 异常 + */ + @SneakyThrows + @Override + public void connectionLost(Throwable throwable) { + while (!client.isConnected()) { + log.info("emqx 重新连接"); + client.connect(option); + Thread.sleep(6000); + } + log.info("emqx 重接成功"); + } + + /** + * 客户端收到消息触发 + * + * @param topic 主题 + * @param message 消息 + */ + @Override + public void messageArrived(String topic, MqttMessage message) throws Exception { + for (SubscriptTopic subscriptTopic : topicMap) { + if (subscriptTopic.getPattern() != Pattern.NONE && isMatched(subscriptTopic.getTopic(), topic)) { + log.info("MqttCallbackImpl,messageArrived:topic=" + topic + ",message=" + message); + subscriptTopic.getMessageListener().messageArrived(topic, message); + break; + } + } + } + + /** + * 检测一个主题是否为一个通配符表示的子主题 + * + * @param topicFilter 通配符主题 + * @param topic 子主题 + * @return 是否为通配符主题的子主题 + */ + private boolean isMatched(String topicFilter, String topic) { + return MqttTopic.isMatched(topicFilter, topic); + } + + /** + * 发布消息成功 + * + * @param token token + */ + @SneakyThrows + @Override + public void deliveryComplete(IMqttDeliveryToken token) { + String[] topics = token.getTopics(); + for (String topic : topics) { + log.info("向主题:" + topic + "发送数据:" + new String(token.getMessage().getPayload())); + } + } + + /** + * 连接emq服务器后触发 + * + * @param b + * @param s + */ + @Override + public void connectComplete(boolean b, String s) { + if (client.isConnected()) { + for (SubscriptTopic sub : topicMap) { + try { + client.subscribe(sub.getSubTopic(), sub.getQos(), sub.getMessageListener()); + log.info("订阅主题:" + sub.getSubTopic() + " Listener:" + sub.getMessageListener()); + } catch (MqttException e) { + //log.info("订阅主题失败:" + sub.getSubTopic()); + e.printStackTrace(); + } + } + log.info("共订阅: " + topicMap.size() + " 个主题!"); + } + } +} diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/framework/mqtt/common/MsgDecoder.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/framework/mqtt/common/MsgDecoder.java new file mode 100644 index 0000000..814160e --- /dev/null +++ b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/framework/mqtt/common/MsgDecoder.java @@ -0,0 +1,17 @@ +package cn.iocoder.yudao.module.iot.framework.mqtt.common; + + +import org.eclipse.paho.client.mqttv3.MqttMessage; + +/** + * @author jie + */ +public interface MsgDecoder { + /** + * 下位机消息解码器 + * + * @param msg + * @return + */ + T decoder(MqttMessage msg); +} diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/framework/mqtt/common/MsgEncoder.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/framework/mqtt/common/MsgEncoder.java new file mode 100644 index 0000000..3f1431b --- /dev/null +++ b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/framework/mqtt/common/MsgEncoder.java @@ -0,0 +1,14 @@ +package cn.iocoder.yudao.module.iot.framework.mqtt.common; + +/** + * @author jie + */ +public interface MsgEncoder { + /** + * 数据库消息编码为string + * + * @param t + * @return + */ + String encoder(T t); +} diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/framework/mqtt/common/SuperConsumer.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/framework/mqtt/common/SuperConsumer.java new file mode 100644 index 0000000..a6a40e8 --- /dev/null +++ b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/framework/mqtt/common/SuperConsumer.java @@ -0,0 +1,37 @@ +package cn.iocoder.yudao.module.iot.framework.mqtt.common; + +import cn.iocoder.yudao.module.iot.framework.mqtt.utils.ThreadUtils; +import lombok.extern.slf4j.Slf4j; +import org.eclipse.paho.client.mqttv3.IMqttMessageListener; +import org.eclipse.paho.client.mqttv3.MqttMessage; + +/** + * 封装的主题消费父类 + * + * @author jie + */ +@Slf4j +public abstract class SuperConsumer implements IMqttMessageListener, MsgDecoder { + @Override + public void messageArrived(String topic, MqttMessage mqttMessage) { +// log.info("收到主题 : " + topic + " 的消息: " + new String(mqttMessage.getPayload())); + ThreadUtils.executorService.submit(() -> { + try { + T decoder = decoder(mqttMessage); + msgHandler(topic, decoder); + } catch (Exception ex) { + //解决业务处理错误导致断线问题 + log.error(topic + ":解决业务处理错误导致断线问题"); + log.error(ex.toString()); + } + }); + } + + /** + * 业务操作 + * + * @param topic + * @param entity + */ + protected abstract void msgHandler(String topic, T entity); +} diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/framework/mqtt/config/DefaultBizTopicSet.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/framework/mqtt/config/DefaultBizTopicSet.java new file mode 100644 index 0000000..c7cb7ac --- /dev/null +++ b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/framework/mqtt/config/DefaultBizTopicSet.java @@ -0,0 +1,17 @@ +package cn.iocoder.yudao.module.iot.framework.mqtt.config; + +import cn.iocoder.yudao.module.iot.framework.mqtt.entity.SubscriptTopic; +import lombok.AllArgsConstructor; +import lombok.Data; +import lombok.NoArgsConstructor; + +import java.util.ArrayList; +import java.util.List; + +// 默认的业务主题集合,使用注解,收集所有的业务处理相关类实例 +@Data +@AllArgsConstructor +@NoArgsConstructor +public class DefaultBizTopicSet { + List topicMap = new ArrayList<>(); +} diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/framework/mqtt/config/DefaultEmqConfig.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/framework/mqtt/config/DefaultEmqConfig.java new file mode 100644 index 0000000..0bbdc75 --- /dev/null +++ b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/framework/mqtt/config/DefaultEmqConfig.java @@ -0,0 +1,99 @@ +package cn.iocoder.yudao.module.iot.framework.mqtt.config; + +import cn.iocoder.yudao.module.iot.framework.mqtt.annotation.Topic; +import cn.iocoder.yudao.module.iot.framework.mqtt.common.MqttCallbackImpl; +import cn.iocoder.yudao.module.iot.framework.mqtt.entity.Pattern; +import cn.iocoder.yudao.module.iot.framework.mqtt.entity.SubscriptTopic; +import cn.iocoder.yudao.module.iot.framework.mqtt.utils.DateUtils; +import lombok.extern.slf4j.Slf4j; +import org.eclipse.paho.client.mqttv3.IMqttMessageListener; +import org.eclipse.paho.client.mqttv3.MqttClient; +import org.eclipse.paho.client.mqttv3.MqttConnectOptions; +import org.eclipse.paho.client.mqttv3.persist.MemoryPersistence; +import org.springframework.boot.context.properties.EnableConfigurationProperties; +import org.springframework.context.ApplicationContext; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.springframework.core.annotation.AnnotationUtils; + +import java.util.ArrayList; +import java.util.List; +import java.util.Map; + +/** + * mqtt配置类 + * + * @author jie + */ +@Slf4j +@Configuration +@EnableConfigurationProperties(DefaultEmqProperties.class) +public class DefaultEmqConfig { + /** + * MQTT的连接设置 + * + * @param emqProperties + * @return + */ + @Bean + public MqttConnectOptions getOption(DefaultEmqProperties emqProperties) { + MqttConnectOptions options = new MqttConnectOptions(); + emqProperties.setClientId("clientId" + String.valueOf(DateUtils.getMillsLong())); + log.info("------mqtt clientid =" + emqProperties.getClientId()); + options.setUserName(emqProperties.getUserName()); + options.setPassword(emqProperties.getPassword().toCharArray()); + // 设置是否清空session,这里如果设置为false表示服务器会保留客户端的连接记录,这里设置为true表示每次连接到服务器都以新的身份连接 + options.setCleanSession(emqProperties.getCleanSession()); + //断线重连 + options.setAutomaticReconnect(emqProperties.getReconnect()); + // 设置超时时间 单位为秒 + options.setConnectionTimeout(emqProperties.getTimeout()); + // 设置会话心跳时间 单位为秒 服务器会每隔1.5*10秒的时间向客户端发送个消息判断客户端是否在线,但这个方法并没有重连的机制 + options.setKeepAliveInterval(emqProperties.getKeepAlive()); + return options; + } + + @Bean + public DefaultBizTopicSet defaultBizTopicSet(ApplicationContext applicationContext) { + List topicMap = new ArrayList<>(); + //得到所有使用@Topic注解的类 + Map beansWithAnnotation = applicationContext.getBeansWithAnnotation(Topic.class); + for (String className : beansWithAnnotation.keySet()) { + Class classByteCode = beansWithAnnotation.get(className).getClass(); + //获取类的注解属性 + Topic annotation = AnnotationUtils.findAnnotation(classByteCode, Topic.class); + String topic = annotation.topic(); + int qos = annotation.qos(); + Pattern patten = annotation.patten(); + String group = annotation.group(); + String subTopic = topic; + if (patten == Pattern.SHARE) { + subTopic = group + "/" + topic; + } else if (patten == Pattern.QUEUE) { + subTopic = topic; + } + topicMap.add(new SubscriptTopic(topic, subTopic, patten, qos, (IMqttMessageListener) applicationContext.getBean(classByteCode))); + } + + return new DefaultBizTopicSet(topicMap); + } + + /** + * 系统默认的MQTT连接 + * + * @param options MQTT连接参数选项 + * @param emqProperties MQTT连接配置 + * @param applicationContext 系统上下文 + * @return + * @throws Exception 异常 + */ + @Bean + public MqttClient mqttClient(MqttConnectOptions options, DefaultEmqProperties emqProperties, DefaultBizTopicSet defaultBizTopicSet, ApplicationContext applicationContext) throws Exception { + MqttClient mqttClient = new MqttClient(emqProperties.getBroker(), emqProperties.getClientId(), new MemoryPersistence()); + mqttClient.setCallback(new MqttCallbackImpl(defaultBizTopicSet.getTopicMap(), mqttClient, options)); + return mqttClient; + } + public static Boolean isEnable(DefaultEmqProperties emqProperties){ + return emqProperties.getIsEnable(); + } +} diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/framework/mqtt/config/DefaultEmqProperties.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/framework/mqtt/config/DefaultEmqProperties.java new file mode 100644 index 0000000..29fdf97 --- /dev/null +++ b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/framework/mqtt/config/DefaultEmqProperties.java @@ -0,0 +1,52 @@ +package cn.iocoder.yudao.module.iot.framework.mqtt.config; + +import lombok.Data; +import org.springframework.boot.context.properties.ConfigurationProperties; + +/** + * 配置类 + * + * @author jie + */ +@Data +@ConfigurationProperties(prefix = "emqx") +public class DefaultEmqProperties { + //public static final String PREFIX = "emqx"; + /** + * emq服务器地址 + */ + private String broker; + /** + * emq client id + */ + private String clientId; + /** + * 用户名 + */ + private String userName; + /** + * 密码 + */ + private String password; + /** + * 设置是否清空session,这里如果设置为false表示服务器会保留客户端的连接记录,这里设置为true表示每次连接到服务器都以新的身份连接 + */ + private Boolean cleanSession; + /** + * 是否断线重连 + */ + private Boolean reconnect; + /** + * 连接超时时间 + */ + private Integer timeout; + /** + * 心跳间隔 + */ + private Integer keepAlive; + /** + * 是否启用 + */ + private Boolean isEnable; + +} diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/framework/mqtt/config/DefaultMqttStarter.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/framework/mqtt/config/DefaultMqttStarter.java new file mode 100644 index 0000000..3e5c980 --- /dev/null +++ b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/framework/mqtt/config/DefaultMqttStarter.java @@ -0,0 +1,39 @@ +package cn.iocoder.yudao.module.iot.framework.mqtt.config; + +import lombok.SneakyThrows; +import lombok.extern.slf4j.Slf4j; +import org.eclipse.paho.client.mqttv3.MqttClient; +import org.eclipse.paho.client.mqttv3.MqttConnectOptions; +import org.eclipse.paho.client.mqttv3.MqttException; +import org.springframework.boot.context.event.ApplicationReadyEvent; +import org.springframework.boot.context.properties.EnableConfigurationProperties; +import org.springframework.context.ApplicationListener; +import org.springframework.stereotype.Component; + +import javax.annotation.Resource; + +@Component +@Slf4j +@EnableConfigurationProperties(DefaultEmqProperties.class) +public class DefaultMqttStarter implements ApplicationListener { + @Resource + private MqttConnectOptions options; + + @Resource + private MqttClient mqttClient; + @Resource + private DefaultEmqProperties defaultEmqProperties; + + @SneakyThrows + @Override + public void onApplicationEvent(ApplicationReadyEvent applicationReadyEvent) { + // catch异常,不然连不上emqx项目就跑不起来 + try { + if(defaultEmqProperties.getIsEnable()) + mqttClient.connect(options); + } catch (MqttException e) { + e.getMessage(); + e.printStackTrace(); + } + } +} diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/framework/mqtt/consumer/IMqttservice.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/framework/mqtt/consumer/IMqttservice.java new file mode 100644 index 0000000..9e7266d --- /dev/null +++ b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/framework/mqtt/consumer/IMqttservice.java @@ -0,0 +1,23 @@ +package cn.iocoder.yudao.module.iot.framework.mqtt.consumer; + +import org.eclipse.paho.client.mqttv3.MqttException; + +public interface IMqttservice { + + /** + * 一次性定时器 + * 程序启动后去数据库找到所有需要订阅的topic 添加进client订阅 + */ + public void subscribeMysqlTopic() throws Exception; + + /** + * 需要订阅的topic 添加进client订阅 + */ + public int subscribeTopic(String topic) throws MqttException; + + /** + * 关闭订阅topic 从client中关闭topic + */ + public int unsubscribeTopic(String topic) throws MqttException; + +} diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/framework/mqtt/consumer/MqttDataHandler.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/framework/mqtt/consumer/MqttDataHandler.java new file mode 100644 index 0000000..031f573 --- /dev/null +++ b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/framework/mqtt/consumer/MqttDataHandler.java @@ -0,0 +1,724 @@ +package cn.iocoder.yudao.module.iot.framework.mqtt.consumer; + + +import cn.iocoder.yudao.framework.tenant.core.context.TenantContextHolder; +import cn.iocoder.yudao.module.iot.controller.admin.device.enums.DeviceBasicStatusEnum; +import cn.iocoder.yudao.module.iot.controller.admin.device.enums.DeviceStatusEnum; +import cn.iocoder.yudao.module.iot.controller.admin.devicemodelrules.vo.PointRulesRespVO; +import cn.iocoder.yudao.module.iot.dal.dataobject.device.DeviceDO; +import cn.iocoder.yudao.module.iot.dal.dataobject.devicecontactmodel.DeviceContactModelDO; +import cn.iocoder.yudao.module.iot.dal.dataobject.deviceoperationrecord.DeviceOperationRecordDO; +import cn.iocoder.yudao.module.iot.dal.dataobject.devicepointrules.DevicePointRulesDO; +import cn.iocoder.yudao.module.iot.dal.dataobject.devicewarinningrecord.DeviceWarinningRecordDO; +import cn.iocoder.yudao.module.iot.dal.mysql.device.DeviceMapper; +import cn.iocoder.yudao.module.iot.dal.mysql.devicecontactmodel.DeviceContactModelMapper; +import cn.iocoder.yudao.module.iot.dal.mysql.deviceoperationrecord.DeviceOperationRecordMapper; +import cn.iocoder.yudao.module.iot.dal.mysql.devicepointrules.DevicePointRulesMapper; +import cn.iocoder.yudao.module.iot.dal.mysql.devicewarinningrecord.DeviceWarinningRecordMapper; +import cn.iocoder.yudao.module.iot.framework.mqtt.common.SuperConsumer; +import cn.iocoder.yudao.module.iot.framework.mqtt.consumer.impl.AsyncService; +import cn.iocoder.yudao.module.iot.framework.mqtt.entity.MqttData; +import cn.iocoder.yudao.module.iot.framework.mqtt.utils.MqttDataUtils; +import cn.iocoder.yudao.module.iot.service.device.TDengineService; +import com.alibaba.fastjson.JSON; +import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; +import com.baomidou.mybatisplus.core.toolkit.CollectionUtils; +import com.baomidou.mybatisplus.core.toolkit.Wrappers; +import com.fasterxml.jackson.databind.JsonNode; +import com.fasterxml.jackson.databind.ObjectMapper; +import lombok.extern.slf4j.Slf4j; +import org.apache.commons.lang3.StringUtils; +import org.eclipse.paho.client.mqttv3.MqttMessage; +import org.springframework.context.annotation.Lazy; +import org.springframework.stereotype.Component; + +import javax.annotation.Resource; +import java.util.ArrayList; +import java.util.List; +import java.util.Map; + +@Slf4j +@Component +public class MqttDataHandler extends SuperConsumer { + + @Resource + @Lazy + private DeviceMapper deviceMapper; + @Resource + private AsyncService asyncService; + + + @Resource + private DeviceOperationRecordMapper deviceOperationRecordMapper; + + @Resource + private DeviceContactModelMapper deviceContactModelMapper; + + @Resource + private TDengineService tDengineService; + + @Resource + private DevicePointRulesMapper devicePointRulesMapper; + + @Resource + private DeviceWarinningRecordMapper deviceWarinningRecordMapper; + + private static final ObjectMapper OBJECT_MAPPER = new ObjectMapper(); + + + + @Override + public String decoder(MqttMessage msg) { + return new String(msg.getPayload()); + } + + @Override + protected void msgHandler(String topic, String entity) { + log.debug("msgHandler" + ":topic=" + topic); + log.debug("entity:" + entity); + + MqttData data = MqttDataUtils.parse(entity); +// IotOrganizationDO machine = new IotOrganizationDO(); +// machine.setId(1L); + if (data != null) { + //根据设定转化 +// try { +// String transfer = asyncService.transferBase(data,equipments.getEquipmentsId()); +// if(StringUtils.isNotBlank(transfer)) +// entity = transfer; +// }catch (Exception e){ +// log.error("asyncService.transferBase error:"+entity); +// } + //异步线程查询时需带TenantId,框架TenantContextHolder限制 + //TODO 后续是否要其他TenantId + try { + // 设置租户ID + TenantContextHolder.setTenantId(1L); + save( entity, data, topic); + } finally { + TenantContextHolder.clear(); + } + } + } + + public void save(String entity, MqttData data,String topic) { +// try { +// long timestamp = DateUtils.strToTimeStamp(data.getDeviceDataTime(), Constants.MQTT_timestamp_format); +// //timestamp = DateUtils.getMillsLong(); +// LocalDateTime date = DateUtils.strToLocalDateTime(data.getDeviceDataTime()); + + // 如果 deviceDataTime 为空,使用当前时间 +// LocalDateTime date; +// long timestamp; +// if (data.getDeviceDataTime() != null && !data.getDeviceDataTime().isEmpty()) { +// try { +// timestamp = DateUtils.strToTimeStamp(data.getDeviceDataTime(), Constants.MQTT_timestamp_format); +// date = DateUtils.strToLocalDateTime(data.getDeviceDataTime()); +// } catch (Exception e) { +// log.warn("解析 deviceDataTime 异常, 使用当前时间: {}", data.getDeviceDataTime(), e); +// timestamp = DateUtils.getMillsLong(); +// date = LocalDateTime.now(); +// } +// } else { +// timestamp = DateUtils.getMillsLong(); +// date = LocalDateTime.now(); +// } +// +// +// +// MqttRecordDO recordDO = new MqttRecordDO(); +// recordDO.setDeviceCode(data.getDeviceID()); +// recordDO.setGatewayCode(data.getGatewayID()); +// recordDO.setDeviceData(entity); +// recordDO.setDeviceDataTime(date); +// recordDO.setDeviceDataTimeLong(timestamp); +// /**直接保存原始mqtt*/ +// mqttRecordMapper.insert(recordDO); + if (StringUtils.isBlank(entity)) { + log.warn("MQTT消息为空 topic={}", topic); + return; + } + + if (StringUtils.isBlank(topic)) { + log.warn("MQTT topic为空"); + return; + } + try { + + + JsonNode rootNode = OBJECT_MAPPER.readTree(entity); + + JsonNode devListNode = rootNode.get("devList"); + + if (devListNode == null || devListNode.isEmpty()) { + return; + } + + JsonNode varListNode = + devListNode.get(0).get("varList"); + + if (varListNode == null) { + return; + } + + Map varListMap = + OBJECT_MAPPER.convertValue( + varListNode, + Map.class + ); + + DeviceDO deviceDO = deviceMapper.selectOne(Wrappers.lambdaQuery().eq(DeviceDO::getTopic,topic)); + log.info("getDeviceByMqttTopic参数:{}", topic); + if (deviceDO == null) { + log.info("getDeviceByMqttTopic查询出来deviceDO为空"); + + return; + } + + + processDeviceDataFromMqtt( + deviceDO, + varListMap + ); + + } catch (Exception e) { + + log.error("MQTT数据处理异常", e); + + } + } + + public void processDeviceDataFromMqtt(DeviceDO device, + Map varListMap) { + + Long deviceId = device.getId(); + + // 1. 查询点位配置 + List points = getDevicePoints(deviceId); + + if (CollectionUtils.isEmpty(points)) { + + log.warn("设备 {} 未配置点位", device.getId()); + +// DeviceOperationRecordDO record = new DeviceOperationRecordDO(); +// record.setDeviceId(deviceId); +// record.setRule(DeviceStatusEnum.STANDBY.getCode()); +// //TODO 待优化 +// record.setTotalStandbyTime(device.getSampleCycle()); +// record.setCreator("1"); +// record.setUpdater("1"); +// +// deviceOperationRecordMapper.insert(record); + + return; + } + + if (varListMap == null || varListMap.isEmpty()) { + + log.warn("设备 {} MQTT varList 为空", deviceId); + return; + } + + // TODO 迁移定时任务存储RUNNING点位规则 +// DevicePointRulesDO devicePoints = getDevicePointRules(deviceId); +// if (StringUtils.isBlank(devicePoints.getFieldRule())){ +// log.warn("设备 {} 没有RUNNING点位规则", device.getId()); +// +// DeviceOperationRecordDO record = new DeviceOperationRecordDO(); +// record.setDeviceId(deviceId); +// record.setRule(DeviceStatusEnum.STANDBY.getCode()); +// //TODO 待优化 +// record.setTotalStandbyTime(device.getSampleCycle()); +// record.setCreator("1"); +// record.setUpdater("1"); +// +// deviceOperationRecordMapper.insert(record); +// +// } + + + + log.info("设备 {} MQTT 数据点位数量 {}", deviceId, varListMap.size()); + + int successCount = 0; + + List validDataList = new ArrayList<>(); + + // 2. 遍历数据库点位,通过 code 匹配 MQTT + for (DeviceContactModelDO point : points) { + + try { + + String code = point.getAttributeCode(); + + Object value = varListMap.get(code); + + if (value == null) { + validDataList.add(point); + continue; + } + + String processedValue = processOpcValue(value); + + // 规则判断 + judgmentRules( + processedValue, + code, + device, + point.getId() + ); + + point.setAddressValue(processedValue); + + successCount++; + + validDataList.add(point); + + } catch (Exception e) { + + log.error("处理 MQTT 点位异常 deviceId={}, code={}", + deviceId, + point.getAttributeCode(), + e); + } + } + + // 3. 入库 + if (!validDataList.isEmpty()) { + + saveToDatabase( + deviceId, + validDataList, + successCount + ); + + } else { + + log.warn("设备 {} 未匹配到 MQTT 数据", deviceId); + + } + } + + + private DevicePointRulesDO getDevicePointRules(Long deviceId) { + + List list = + devicePointRulesMapper.selectList( + Wrappers.lambdaQuery() + .eq(DevicePointRulesDO::getDeviceId, deviceId) + .eq(DevicePointRulesDO::getIdentifier, "RUNNING") + .orderByDesc(DevicePointRulesDO::getCreateTime) + .last("LIMIT 1") + ); + + if (CollectionUtils.isEmpty(list)) { + + log.info("设备 {} 未找到 RUNNING 规则", deviceId); + + return null; + } + + DevicePointRulesDO rule = list.get(0); + + log.info("设备 {} 使用 RUNNING 规则,规则ID={}, 创建时间={}", + deviceId, + rule.getId(), + rule.getCreateTime()); + + return rule; + } + + + + /** + * 获取设备点位 + */ + private List getDevicePoints(Long deviceId) { + LambdaQueryWrapper query = new LambdaQueryWrapper<>(); + query.eq(DeviceContactModelDO::getDeviceId, deviceId); + return deviceContactModelMapper.selectList(query); + } + + + /** + * 处理OPC值 + */ + private String processOpcValue(Object value) { + if (value == null) { + return ""; + } + + if (value instanceof String) { + return ((String) value).trim(); + } + + return value.toString(); + } + + /** + * 保存到数据库 + */ + private void saveToDatabase(Long deviceId, List dataList, int successCount) { + try { + for (DeviceContactModelDO deviceContactModelDO : dataList) { + deviceContactModelDO.setAddress(null); + } + String json = JSON.toJSONString(dataList); +// boolean inserted = tDengineService.insertDeviceData(deviceId, json); + + boolean isSuccess = tDengineService.newInsertDeviceData(deviceId, dataList); + + if (isSuccess) { + log.info("设备 {} 数据入库成功,总数: {},有效: {}", + deviceId, dataList.size(), successCount); + } else { + log.error("设备 {} 数据入库失败", deviceId); + } + } catch (Exception e) { + log.error("设备 {} 数据入库异常", deviceId, e); + } + } + + private void judgmentRules(String processedValue, String attributeCode, DeviceDO device, Long modelId) { + if (StringUtils.isBlank(processedValue)) { + log.warn("待判断的值为空,编码attributeCode: {}, deviceId: {}", attributeCode, device.getId()); +// return; + } + + // 1. 查询设备规则 + List devicePointRulesDOList = devicePointRulesMapper.selectList( + Wrappers.lambdaQuery() + .eq(DevicePointRulesDO::getDeviceId, device.getId()).orderByDesc(DevicePointRulesDO::getCreateTime)); + + if (CollectionUtils.isEmpty(devicePointRulesDOList)) { + log.debug("设备 {} 未配置规则", device.getId()); + return; + } + + // 2. 遍历规则 + for (DevicePointRulesDO devicePointRulesDO : devicePointRulesDOList) { + if (StringUtils.isBlank(devicePointRulesDO.getFieldRule())) { + continue; + } + + // 3. 解析规则列表 + List pointRulesVOList = JSON.parseArray( + devicePointRulesDO.getFieldRule(), PointRulesRespVO.class); + + if (CollectionUtils.isEmpty(pointRulesVOList)) { + continue; + } + + // 4. 找到对应modelId的规则并进行判断 + for (PointRulesRespVO pointRulesRespVO : pointRulesVOList) { + if (pointRulesRespVO.getCode() != null && + pointRulesRespVO.getCode().equals(attributeCode)) { + + boolean matched = matchRule(processedValue, pointRulesRespVO); + + if (matched) { + log.info("规则匹配成功: modelId={}, value={}, rule={}", + attributeCode, processedValue, + JSON.toJSONString(pointRulesRespVO)); + + // 执行匹配成功后的逻辑 + handleMatchedSuccessRule(devicePointRulesDO, pointRulesRespVO, processedValue, device, attributeCode, modelId); + break; + } else { + log.debug("规则不匹配: modelId={}, value={}, rule={}", + attributeCode, processedValue, + JSON.toJSONString(pointRulesRespVO)); + // 执行匹配失败后的逻辑 + handleMatchedFailureRule(devicePointRulesDO, pointRulesRespVO, processedValue, device, attributeCode); + + } + } + } + } + } + + private void handleMatchedFailureRule(DevicePointRulesDO devicePointRulesDO, PointRulesRespVO pointRulesRespVO, String processedValue, DeviceDO device, String attributeCode) { + //TODO 离线待优化 +// if (devicePointRulesDO.getIdentifier().equals(DeviceBasicStatusEnum.RUNNING.getCode())){ +// DeviceOperationRecordDO record = new DeviceOperationRecordDO(); +// record.setDeviceId(device.getId()); +// record.setModelId(modelId); +// record.setRule(pointRulesRespVO.getRule()); +// record.setAddressValue(processedValue); +// record.setRecordType(getRecordType(devicePointRulesDO)); +// record.setRuleId(devicePointRulesDO.getId()); +// +// } + + + } + + private void handleMatchedSuccessRule(DevicePointRulesDO devicePointRulesDO, + PointRulesRespVO pointRulesRespVO, + String processedValue, + DeviceDO device, + String attributeCode, + Long modelId) { + DeviceContactModelDO deviceContactModelDO = deviceContactModelMapper.selectById(modelId); + if (deviceContactModelDO == null) { + return; + } + + + //分别处理运行记录和告警记录 + if (StringUtils.isBlank(devicePointRulesDO.getAlarmLevel())) { + //TODO 迁移运行记录到定时任务 +// DeviceOperationRecordDO record = new DeviceOperationRecordDO(); +// record.setDeviceId(device.getId()); +// record.setModelId(modelId); +// record.setRule(pointRulesRespVO.getRule()); +// record.setAddressValue(processedValue); +// record.setRecordType(getRecordType(devicePointRulesDO)); +// record.setRuleId(devicePointRulesDO.getId()); +// //TODO 创建人和更新人为内置默认管理员 +// record.setCreator("1"); +// record.setUpdater("1"); +// +// // 处理累计时间 +// calculateAndSetTotalTime(record, pointRulesRespVO.getRule(), device.getSampleCycle()); +// +// deviceOperationRecordMapper.insert(record); + } else { + + DeviceWarinningRecordDO deviceWarinningRecordDO = new DeviceWarinningRecordDO(); + deviceWarinningRecordDO.setDeviceId(device.getId()); + deviceWarinningRecordDO.setModelId(modelId); + deviceWarinningRecordDO.setRule(pointRulesRespVO.getRule()); + deviceWarinningRecordDO.setAlarmLevel(devicePointRulesDO.getAlarmLevel()); + deviceWarinningRecordDO.setAddressValue(processedValue); + deviceWarinningRecordDO.setRuleId(devicePointRulesDO.getId()); + deviceWarinningRecordDO.setDeviceName(device.getDeviceName()); + deviceWarinningRecordDO.setModelName(deviceContactModelDO.getAttributeName()); + deviceWarinningRecordDO.setRuleName(devicePointRulesDO.getFieldName()); + //TODO 创建人和更新人为内置默认管理员 + deviceWarinningRecordDO.setCreator("1"); + deviceWarinningRecordDO.setUpdater("1"); +// deviceWarinningRecordMapper.insert(deviceWarinningRecordDO); + tDengineService.insertDeviceWarningRecord(deviceWarinningRecordDO); + } + } + + private void calculateAndSetTotalTime(DeviceOperationRecordDO record, String ruleCode, Double sampleCycle) { + if (!isTimeRelatedStatus(ruleCode)) { + return; + } + +// DeviceOperationRecordDO lastRecord = deviceOperationRecordMapper.selectOne( +// Wrappers.lambdaQuery() +// .eq(DeviceOperationRecordDO::getRule, ruleCode) +// .orderByDesc(DeviceOperationRecordDO::getCreateTime) +// .last("LIMIT 1") +// ); + + if (ruleCode.equals(DeviceStatusEnum.RUNNING.getCode())) { +// Double totalTime = (lastRecord != null && lastRecord.getTotalRunningTime() != null) +// ? lastRecord.getTotalRunningTime() + sampleCycle +// : sampleCycle; + record.setTotalRunningTime(sampleCycle); + + } else if (ruleCode.equals(DeviceStatusEnum.STANDBY.getCode())) { +// Double totalTime = (lastRecord != null && lastRecord.getTotalStandbyTime() != null) +// ? lastRecord.getTotalStandbyTime() + sampleCycle +// : sampleCycle; + record.setTotalStandbyTime(sampleCycle); + + } else if (ruleCode.equals(DeviceStatusEnum.FAULT_STANDBY.getCode())) { +// Double totalTime = (lastRecord != null && lastRecord.getTotalFaultTime() != null) +// ? lastRecord.getTotalFaultTime() + sampleCycle +// : sampleCycle; + record.setTotalFaultTime(sampleCycle); + + } else if (ruleCode.equals(DeviceStatusEnum.ALARM_RUNNING.getCode())) { +// Double totalTime = (lastRecord != null && lastRecord.getTotalWarningTime() != null) +// ? lastRecord.getTotalWarningTime() + sampleCycle +// : sampleCycle; + record.setTotalWarningTime(sampleCycle); + } + } + + private Integer getRecordType(DevicePointRulesDO devicePointRulesDO) { + return devicePointRulesDO.getIdentifier() + .equals(DeviceBasicStatusEnum.RUNNING.getDescription()) + ? Integer.parseInt(DeviceBasicStatusEnum.RUNNING.getCode()) + : Integer.parseInt(DeviceBasicStatusEnum.ALARM.getCode()); + } + + private boolean isTimeRelatedStatus(String ruleCode) { + return ruleCode.equals(DeviceStatusEnum.RUNNING.getCode()) || + ruleCode.equals(DeviceStatusEnum.STANDBY.getCode()) || + ruleCode.equals(DeviceStatusEnum.FAULT_STANDBY.getCode()) || + ruleCode.equals(DeviceStatusEnum.ALARM_RUNNING.getCode()); + } + + + /** + * 判断值是否符合规则 + * 支持操作符: EQ(等于), NE(不等于), GT(大于), GE(大于等于), + * LT(小于), LE(小于等于), TRUE(为真), FALSE(为假) + */ + private boolean matchRule(String value, PointRulesRespVO rule) { + if (StringUtils.isBlank(value) || rule == null || + StringUtils.isBlank(rule.getOperator())) { + return false; + } + + try { + String operator = rule.getOperator().toUpperCase(); + String inputValue = value.trim().toLowerCase(); + String ruleValue = StringUtils.trimToEmpty(rule.getOperatorRule()); + + // 1. 处理布尔值判断 + if ("TRUE".equals(operator) || "FALSE".equals(operator)) { + return matchBooleanRule(inputValue, operator); + } + + // 2. 如果operatorRule为空,且不是布尔操作符,则返回false + if (StringUtils.isBlank(ruleValue)) { + log.warn("规则比较值为空,但操作符不是布尔类型: operator={}", operator); + return false; + } + + ruleValue = ruleValue.trim(); + + // 3. 尝试数值比较 + if (isNumeric(inputValue) && isNumeric(ruleValue)) { + Double num1 = Double.parseDouble(inputValue); + Double num2 = Double.parseDouble(ruleValue); + + return compareNumbers(num1, num2, operator); + } + // 4. 字符串比较 + else { + return compareStrings(inputValue, ruleValue, operator); + } + + } catch (Exception e) { + log.error("规则匹配异常: value={}, rule={}, error={}", + value, JSON.toJSONString(rule), e.getMessage()); + return false; + } + } + + /** + * 处理布尔值判断 + */ + private boolean matchBooleanRule(String value, String operator) { + // 常见布尔值表示 + boolean booleanValue = parseBoolean(value); + + if ("TRUE".equals(operator)) { + return booleanValue; + } else if ("FALSE".equals(operator)) { + return !booleanValue; + } + + return false; + } + + /** + * 解析字符串为布尔值 + * 支持: true, false, 1, 0, yes, no, on, off等 + */ + private boolean parseBoolean(String value) { + if (StringUtils.isBlank(value)) { + return false; + } + + String lowerValue = value.toLowerCase(); + + // 常见真值表示 + if ("true".equals(lowerValue) || + "1".equals(lowerValue) || + "yes".equals(lowerValue) || + "on".equals(lowerValue) || + "是".equals(lowerValue) || // 中文支持 + "成功".equals(lowerValue)) { + return true; + } + + // 常见假值表示 + if ("false".equals(lowerValue) || + "0".equals(lowerValue) || + "no".equals(lowerValue) || + "off".equals(lowerValue) || + "否".equals(lowerValue) || // 中文支持 + "失败".equals(lowerValue)) { + return false; + } + + // 尝试转换为布尔值 + try { + return Boolean.parseBoolean(lowerValue); + } catch (Exception e) { + log.warn("无法解析为布尔值: {}", value); + return false; + } + } + + /** + * 数值比较 + */ + private boolean compareNumbers(Double value, Double ruleValue, String operator) { + switch (operator) { + case "EQ": + return Math.abs(value - ruleValue) < 0.000001; // 处理浮点数精度 + case "NE": + return Math.abs(value - ruleValue) >= 0.000001; + case "GT": + return value > ruleValue; + case "GE": + return value >= ruleValue; + case "LT": + return value < ruleValue; + case "LE": + return value <= ruleValue; + default: + log.warn("不支持的操作符: {}", operator); + return false; + } + } + + /** + * 字符串比较 + */ + private boolean compareStrings(String value, String ruleValue, String operator) { + switch (operator) { + case "EQ": + return value.equals(ruleValue); + case "NE": + return !value.equals(ruleValue); + case "GT": + return value.compareTo(ruleValue) > 0; + case "GE": + return value.compareTo(ruleValue) >= 0; + case "LT": + return value.compareTo(ruleValue) < 0; + case "LE": + return value.compareTo(ruleValue) <= 0; + default: + log.warn("不支持的操作符: {}", operator); + return false; + } + } + + /** + * 判断字符串是否为数字 + */ + private boolean isNumeric(String str) { + if (StringUtils.isBlank(str)) { + return false; + } + try { + Double.parseDouble(str); + return true; + } catch (NumberFormatException e) { + return false; + } + } + +} diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/framework/mqtt/consumer/impl/AsyncService.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/framework/mqtt/consumer/impl/AsyncService.java new file mode 100644 index 0000000..03b84cf --- /dev/null +++ b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/framework/mqtt/consumer/impl/AsyncService.java @@ -0,0 +1,156 @@ +package cn.iocoder.yudao.module.iot.framework.mqtt.consumer.impl; + +import cn.iocoder.yudao.module.iot.framework.mqtt.entity.MqttData; +import cn.iocoder.yudao.module.iot.service.device.DeviceService; +import lombok.extern.slf4j.Slf4j; +import org.apache.commons.lang3.StringUtils; +import org.springframework.context.annotation.Configuration; +import org.springframework.context.annotation.Lazy; +import org.springframework.scheduling.annotation.EnableAsync; +import org.springframework.scheduling.annotation.EnableScheduling; + +import javax.annotation.Resource; +import java.util.Calendar; +import java.util.Date; +import java.util.HashMap; +import java.util.Map; + +@Configuration +@EnableAsync +@Slf4j +@EnableScheduling +public class AsyncService { + @Resource + @Lazy + private DeviceService deviceService; + + + /**地址转换进制*/ + //@Async +// public String transferBase(MqttData data, String equipmentId) { +// //data数据改变标记 +// boolean isTransfer = false; +// IoPoint vo = new IoPoint(); +// vo.setEquipmentsId(equipmentId); +// vo.setDeviceId(data.getDeviceID()); +// vo.setTransferDataType("---"); +// List pointList = ioPointService.list(ioPointService.buildQueryWrapper(vo)); +// if(pointList!=null && pointList.size()>0){ +// //先转进制 +// for (IoPoint point: pointList) { +// if(data.getDeviceData().containsKey(point.getPointCode()) && StringUtils.isNotBlank(point.getSourceDataType())){ +// String value = data.getDeviceData().get(point.getPointCode()).getV(); +// //防止有些数乱填 +// if(value.contains("."))continue; +// if(StringUtils.isNotBlank(value) && StringUtils.isNotBlank(point.getSourceDataType())){ +// isTransfer = true; +// value = transferBase(value,point.getSourceDataType(),point.getTransferDataType()); +// value = factor(value,point.getFactor()); +// data.getDeviceData().get(point.getPointCode()).setV(value); +// } +// } +// } +// } +// //再进行公式计算 +// Map valueMap = getValueMap(data); +// vo.setTransferDataType(""); +// vo.setCalAddresses("---"); +// pointList = ioPointService.list(ioPointService.buildQueryWrapper(vo)); +// for (IoPoint point: pointList) { +// if(data.getDeviceData().containsKey(point.getPointCode()) && StringUtils.isNotBlank(point.getCalAddresses())){ +// try { +// isTransfer = true; +// String result = FormulaCalculator.calFormula(point.getCalAddresses(),valueMap); +// data.getDeviceData().get(point.getPointCode()).setV(result); +// } catch (ScriptException e) { +// log.error("calFormula point:"+point.toString()); +// } +// } +// } +// if(isTransfer) +// return JSON.toJSONString(data); +// else +// return null; +// } + + /** + * 系数 + **/ + private String factor(String value, String factor) { + if (StringUtils.isNotBlank(factor)) { + return String.format("%.2f", Double.parseDouble(value) * Double.parseDouble(factor)); + } + return value; + } + + private Map getValueMap(MqttData data) { + Map valueMap = new HashMap<>(); + for (String key : data.getDeviceData().keySet()) { + valueMap.put(key, data.getDeviceData().get(key).getV()); + } + return valueMap; + } + + /** + * java 代码将给定的值value,从一个进制转换到另一个进制,进制取值可能为binary,octal,hexadecimal,decimal等 + */ + private String transferBase(String value, String originBase, String transferBase) { + switch (transferBase) { + case "decimal": { + switch (originBase) { + case "binary": + value = convertBase(value, 2, 10); + break; + case "octal": + value = convertBase(value, 8, 10); + break; + case "hexadecimal": + value = convertBase(value, 16, 10); + case "hexfloat": + value = hexToDecimal(value); + } + } + break; + default: + log.debug("数据未进行转换!:(value,originBase,transferBase)(" + value + "," + originBase + "," + transferBase + ")"); + } + return value; + } + + private static String hexToDecimal(String value) { + return String.valueOf(Integer.parseInt(value, 16)); + } + + + public static String convertBase(String value, int fromBase, int toBase) { + return String.valueOf(Integer.parseInt(value, fromBase)); + } + + public static void main(String[] args) { + String value = "479624"; // 要转换的值 + int fromBase = 16; // 起始进制 + int toBase = 10; // 目标进制 + + String convertedValue = convertBase(value, fromBase, toBase); + System.out.println("Converted value: " + convertedValue); + + + int intValue = Integer.parseInt(value, 16); // 将十六进制字符串解析为十进制整数 + System.out.println(intValue); + + intValue = Integer.parseInt("476c", 16); // 将十六进制字符串解析为十进制整数 + System.out.println(intValue); + + // 获取当前日期 + Calendar calendar = Calendar.getInstance(); + calendar.set(Calendar.HOUR_OF_DAY, 0); + calendar.set(Calendar.MINUTE, 0); + calendar.set(Calendar.SECOND, 0); + calendar.set(Calendar.MILLISECOND, 0); + + Date date = calendar.getTime(); + long timestamp = date.getTime() / 1000; // 将时间戳转换为秒 + + System.out.println("当天 00:00:00 的时间戳:" + timestamp); + } +} diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/framework/mqtt/consumer/impl/MqttserviceImpl.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/framework/mqtt/consumer/impl/MqttserviceImpl.java new file mode 100644 index 0000000..1bddff6 --- /dev/null +++ b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/framework/mqtt/consumer/impl/MqttserviceImpl.java @@ -0,0 +1,135 @@ +package cn.iocoder.yudao.module.iot.framework.mqtt.consumer.impl; + +import cn.iocoder.yudao.framework.tenant.core.context.TenantContextHolder; +import cn.iocoder.yudao.module.iot.dal.dataobject.gateway.GatewayDO; +import cn.iocoder.yudao.module.iot.framework.constant.IsEnableConstant; +import cn.iocoder.yudao.module.iot.framework.mqtt.common.MqttCallbackImpl; +import cn.iocoder.yudao.module.iot.framework.mqtt.config.DefaultBizTopicSet; +import cn.iocoder.yudao.module.iot.framework.mqtt.consumer.IMqttservice; +import cn.iocoder.yudao.module.iot.framework.mqtt.consumer.MqttDataHandler; +import cn.iocoder.yudao.module.iot.framework.mqtt.entity.Pattern; +import cn.iocoder.yudao.module.iot.framework.mqtt.entity.SubscriptTopic; +import cn.iocoder.yudao.module.iot.service.gateway.GatewayService; +import lombok.extern.slf4j.Slf4j; +import org.apache.commons.lang3.StringUtils; +import org.eclipse.paho.client.mqttv3.MqttClient; +import org.eclipse.paho.client.mqttv3.MqttConnectOptions; +import org.eclipse.paho.client.mqttv3.MqttException; +import org.springframework.boot.context.event.ApplicationReadyEvent; +import org.springframework.context.ApplicationListener; +import org.springframework.context.annotation.DependsOn; +import org.springframework.context.annotation.Lazy; +import org.springframework.scheduling.annotation.EnableScheduling; +import org.springframework.stereotype.Service; + +import javax.annotation.Resource; +import java.util.List; + +@Service +@EnableScheduling +@DependsOn("defaultMqttStarter") +@Slf4j +@Lazy +public class MqttserviceImpl implements IMqttservice, ApplicationListener { + + @Resource + private GatewayService gatewayService; + + @Resource + private MqttClient client; + @Resource + private DefaultBizTopicSet defaultBizTopicSet; + @Resource + private MqttConnectOptions options; + + @Resource + private MqttDataHandler dataHandler; + + + @Override + public void onApplicationEvent(ApplicationReadyEvent applicationReadyEvent) { +// new Thread(() -> { +// subscribeMysqlTopic(); +// }).start(); + new Thread(() -> { + try { + TenantContextHolder.setTenantId(1L); + subscribeMysqlTopic(); + } finally { + TenantContextHolder.clear(); + } + }).start(); + } + + /** + * 一次性定时器 + * 程序启动后去数据库找到所有需要订阅的topic 添加进client订阅 + */ + @Override + public void subscribeMysqlTopic() { + if (!client.isConnected()) { + try { + log.warn("MQTT未连接,尝试重新连接..."); + client.reconnect(); + } catch (Exception e) { + log.error("MQTT重连失败", e); + return; + } + } + + List gatewayList = gatewayService.selectListByIsEnable(IsEnableConstant.IsEnableTrue); + for (GatewayDO gateway : gatewayList) { + if (StringUtils.isNotBlank(gateway.getTopic())) { + SubscriptTopic topic = new SubscriptTopic(gateway.getTopic(), + gateway.getTopic(), Pattern.QUEUE, 0, dataHandler); + defaultBizTopicSet.getTopicMap().add(topic); + try { + client.subscribe(topic.getSubTopic(), 0, dataHandler); + } catch (MqttException e) { + e.printStackTrace(); + } + } + } + client.setCallback(new MqttCallbackImpl(defaultBizTopicSet.getTopicMap(), client, options)); + log.info("共订阅: " + defaultBizTopicSet.getTopicMap().size() + " 个主题!"); + + + } + + /** + * 需要订阅的topic 添加进client订阅 + */ + @Override + public int subscribeTopic(String topic) throws MqttException { + + //断线重连 + if (!client.isConnected()) { + log.warn("MQTT未连接,尝试重新连接..."); + client.reconnect(); + } + + SubscriptTopic subscriptTopic = new SubscriptTopic(topic, topic, Pattern.QUEUE, 0, dataHandler); + defaultBizTopicSet.getTopicMap().add(subscriptTopic); + client.subscribe(topic, 0, dataHandler); + client.setCallback(new MqttCallbackImpl(defaultBizTopicSet.getTopicMap(), client, options)); + return 0; + } + + /** + * 关闭订阅topic 从client中关闭topic + */ + @Override + public int unsubscribeTopic(String topic) throws MqttException { + for (int i = 0; i < defaultBizTopicSet.getTopicMap().size(); i++) { + if (topic.equals(defaultBizTopicSet.getTopicMap().get(i).getSubTopic())) { + defaultBizTopicSet.getTopicMap().remove(defaultBizTopicSet.getTopicMap().get(i)); + break; + } + } + client.unsubscribe(topic); + client.setCallback(new MqttCallbackImpl(defaultBizTopicSet.getTopicMap(), client, options)); + return 0; + } + + +} diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/framework/mqtt/entity/Connect.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/framework/mqtt/entity/Connect.java new file mode 100644 index 0000000..a9a994f --- /dev/null +++ b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/framework/mqtt/entity/Connect.java @@ -0,0 +1,56 @@ +package cn.iocoder.yudao.module.iot.framework.mqtt.entity; + +import lombok.AllArgsConstructor; +import lombok.Data; +import lombok.NoArgsConstructor; +import lombok.ToString; + +/** + *

设备上线 + * 邮箱:275236367@qq.com + * 创建时间: 2020/1/4 + * + * @author jie + */ +@Data +@ToString +@NoArgsConstructor +@AllArgsConstructor +public class Connect { + /** + * 客户端id + */ + private String clientid; + /** + * 用户名 + */ + private String username; + /** + * ip地址 + */ + private String ipaddress; + /** + * 连接回执 + */ + private int connack; + /** + * 事件触发时间 (ms) + */ + private long ts; + /** + * 协议版本 + */ + private int proto_ver; + /** + * 协议名字 + */ + private String proto_name; + /** + * MQTT clean_start + */ + private boolean clean_start; + /** + * 保持连接 + */ + private int keepalive; +} diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/framework/mqtt/entity/Disconnect.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/framework/mqtt/entity/Disconnect.java new file mode 100644 index 0000000..918a28a --- /dev/null +++ b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/framework/mqtt/entity/Disconnect.java @@ -0,0 +1,34 @@ +package cn.iocoder.yudao.module.iot.framework.mqtt.entity; + +import lombok.AllArgsConstructor; +import lombok.Data; +import lombok.NoArgsConstructor; +import lombok.ToString; + +/** + *

设备下线 + * + * @author jie + */ +@Data +@ToString +@NoArgsConstructor +@AllArgsConstructor +public class Disconnect { + /** + * 客户端id + */ + private String clientid; + /** + * 用户名 + */ + private String username; + /** + * 终端连接断开原因 + */ + private String reason; + /** + * 事件触发时间 (ms) + */ + private long ts; +} diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/framework/mqtt/entity/IoData.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/framework/mqtt/entity/IoData.java new file mode 100644 index 0000000..c683189 --- /dev/null +++ b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/framework/mqtt/entity/IoData.java @@ -0,0 +1,13 @@ +package cn.iocoder.yudao.module.iot.framework.mqtt.entity; + +import lombok.AllArgsConstructor; +import lombok.Data; +import lombok.NoArgsConstructor; + +@Data +@AllArgsConstructor +@NoArgsConstructor +public class IoData { + String u; + String v; +} diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/framework/mqtt/entity/MqttData.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/framework/mqtt/entity/MqttData.java new file mode 100644 index 0000000..3ff172c --- /dev/null +++ b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/framework/mqtt/entity/MqttData.java @@ -0,0 +1,19 @@ +package cn.iocoder.yudao.module.iot.framework.mqtt.entity; + +import lombok.AllArgsConstructor; +import lombok.Data; +import lombok.NoArgsConstructor; + +import java.util.Map; + +// 同一的数据格式 +@Data +@AllArgsConstructor +@NoArgsConstructor +public class MqttData { + protected String deviceID; + protected String gatewayID; + protected Map deviceData; + protected String deviceDataTime; + +} diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/framework/mqtt/entity/Pattern.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/framework/mqtt/entity/Pattern.java new file mode 100644 index 0000000..ed385be --- /dev/null +++ b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/framework/mqtt/entity/Pattern.java @@ -0,0 +1,22 @@ +package cn.iocoder.yudao.module.iot.framework.mqtt.entity; + +/** + * 订阅模式 + * + * @author jie + */ + +public enum Pattern { + /** + * 普通订阅 + */ + NONE, + /** + * 不带群组的共享订阅 + */ + QUEUE, + /** + * 带群组的共享订阅 + */ + SHARE; +} diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/framework/mqtt/entity/SubscriptTopic.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/framework/mqtt/entity/SubscriptTopic.java new file mode 100644 index 0000000..4a55c8b --- /dev/null +++ b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/framework/mqtt/entity/SubscriptTopic.java @@ -0,0 +1,36 @@ +package cn.iocoder.yudao.module.iot.framework.mqtt.entity; + +import lombok.*; +import org.eclipse.paho.client.mqttv3.IMqttMessageListener; + +/** + * @author jie + */ +@Data +@Builder +@ToString +@NoArgsConstructor +@AllArgsConstructor +public class SubscriptTopic { + /** + * 原主题 + */ + private String topic; + /** + * 订阅主题 + */ + private String subTopic; + /** + * 订阅模式 + */ + private Pattern pattern; + /** + * 消息等级 + */ + private int qos; + /** + * 消费类 + */ + private IMqttMessageListener messageListener; + +} diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/framework/mqtt/utils/ApplicationContextUtil.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/framework/mqtt/utils/ApplicationContextUtil.java new file mode 100644 index 0000000..02f99d7 --- /dev/null +++ b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/framework/mqtt/utils/ApplicationContextUtil.java @@ -0,0 +1,50 @@ +package cn.iocoder.yudao.module.iot.framework.mqtt.utils; + +import org.springframework.beans.BeansException; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.context.ApplicationContext; +import org.springframework.stereotype.Component; + +import javax.annotation.PostConstruct; + +/** + * 类名称:ApplicationContextUtil + * 类描述: + *

+ *

从容器中获取bean + */ +@Component +public class ApplicationContextUtil { + + private static ApplicationContextUtil instance; + @Autowired + private ApplicationContext applicationContext; + + @PostConstruct + public void applicationContextUtil() { + instance = this; + } + + /** + * 根据字节码获取bean + * + * @param clazz + * @param + * @return + */ + public static T getBean(Class clazz) { + return instance.applicationContext.getBean(clazz); + } + + /** + * 根据名字获取bean + * + * @param name + * @param + * @return + * @throws BeansException + */ + public static T getBean(String name) throws BeansException { + return (T) instance.applicationContext.getBean(name); + } +} diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/framework/mqtt/utils/DateUtils.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/framework/mqtt/utils/DateUtils.java new file mode 100644 index 0000000..ef1ed0e --- /dev/null +++ b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/framework/mqtt/utils/DateUtils.java @@ -0,0 +1,290 @@ +package cn.iocoder.yudao.module.iot.framework.mqtt.utils; + +import org.apache.commons.lang3.time.DateFormatUtils; + +import java.lang.management.ManagementFactory; +import java.text.ParseException; +import java.text.SimpleDateFormat; +import java.time.LocalDateTime; +import java.time.format.DateTimeFormatter; +import java.util.Calendar; +import java.util.Date; +import java.util.TimeZone; + +public class DateUtils extends org.apache.commons.lang3.time.DateUtils { + /** + * 将本地时间, 转换成目标时区的时间 + * + * @param sourceDate + * @return + */ + public static Date convertTimezone(Date sourceDate, String targetZoneId) { + return convertTimezone(sourceDate, TimeZone.getTimeZone(targetZoneId)); + } + + public static Date convertTimezone(Date sourceDate, String sourceZoneId, String targetZoneId) { + TimeZone sourceTimeZone = TimeZone.getTimeZone(sourceZoneId); + TimeZone targetTimeZone = TimeZone.getTimeZone(targetZoneId); + + return convertTimezone(sourceDate, sourceTimeZone, targetTimeZone); + } + + /** + * 将本地时间,转换成对应时区的时间 + * + * @param localDate + * @param targetTimezone 转换成目标时区所在的时间 + * @return + */ + public static Date convertTimezone(Date localDate, TimeZone targetTimezone) { + return convertTimezone(localDate, TimeZone.getDefault(), targetTimezone); + } + + + /** + * 将sourceDate转换成指定时区的时间 + * + * @param sourceDate + * @param sourceTimezone sourceDate所在的时区 + * @param targetTimezone 转化成目标时间所在的时区 + * @return + */ + public static Date convertTimezone(Date sourceDate, TimeZone sourceTimezone, TimeZone targetTimezone) { + + + // targetDate - sourceDate=targetTimezone-sourceTimezone + // ---> + // targetDate=sourceDate + (targetTimezone-sourceTimezone) + + + Calendar calendar = Calendar.getInstance(); + // date.getTime() 为时间戳, 为格林尼治到系统现在的时间差,世界各个地方获取的时间戳是一样的, + // 格式化输出时,因为设置了不同的时区,所以输出不一样 + long sourceTime = sourceDate.getTime(); + + + calendar.setTimeZone(sourceTimezone); + calendar.setTimeInMillis(sourceTime);// 设置之后,calendar会计算各种filed对应的值,并保存 + + //获取源时区的到UTC的时区差 + int sourceZoneOffset = calendar.get(Calendar.ZONE_OFFSET); + + + calendar.setTimeZone(targetTimezone); + calendar.setTimeInMillis(sourceTime); + + int targetZoneOffset = calendar.get(Calendar.ZONE_OFFSET); + int targetDaylightOffset = calendar.get(Calendar.DST_OFFSET); // 夏令时 + + + long targetTime = sourceTime + (targetZoneOffset + targetDaylightOffset) - sourceZoneOffset; + + return new Date(targetTime); + + } + + public static String addDateMinut(String day, int hour) { + SimpleDateFormat format = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss"); + Date date = null; + try { + date = format.parse(day); + } catch (Exception ex) { + ex.printStackTrace(); + } + if (date == null) + return ""; + Calendar cal = Calendar.getInstance(); + cal.setTime(date); + cal.add(Calendar.HOUR, -hour);// 24小时制 + date = cal.getTime(); + cal = null; + return format.format(date); + } + + public static String addDateDay(String time, int day) { + SimpleDateFormat format = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss"); + Date date = null; + try { + date = format.parse(time); + } catch (Exception ex) { + ex.printStackTrace(); + } + if (date == null) + return ""; + Calendar cal = Calendar.getInstance(); + cal.setTime(date); + cal.add(Calendar.DAY_OF_MONTH, -day);// 日 + date = cal.getTime(); + cal = null; + return format.format(date); + } + + + public static String addDateMonth(String day, int hour) { + SimpleDateFormat format = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss"); + Date date = null; + try { + date = format.parse(day); + } catch (Exception ex) { + ex.printStackTrace(); + } + if (date == null) + return ""; + Calendar cal = Calendar.getInstance(); + cal.setTime(date); + cal.add(Calendar.MONTH, hour);// 月 + date = cal.getTime(); + cal = null; + return format.format(date); + } + + public static String YYYY = "yyyy"; + + public static String YYYY_MM = "yyyy-MM"; + + public static String YYYY_MM_DD = "yyyy-MM-dd"; + + public static String YYYYMMDDHHMMSS = "yyyyMMddHHmmss"; + + public static String YYYY_MM_DD_HH_MM_SS = "yyyy-MM-dd HH:mm:ss"; + public static String YYYY_MM_DD_HH_MM_SS_SSS = "yyyy-MM-dd HH:mm:ss.SSS"; + private static String[] parsePatterns = { + "yyyy-MM-dd", "yyyy-MM-dd HH:mm:ss", "yyyy-MM-dd HH:mm", "yyyy-MM", + "yyyy/MM/dd", "yyyy/MM/dd HH:mm:ss", "yyyy/MM/dd HH:mm", "yyyy/MM", + "yyyy.MM.dd", "yyyy.MM.dd HH:mm:ss", "yyyy.MM.dd HH:mm", "yyyy.MM"}; + + /** + * 获取当前Date型日期 + * + * @return Date() 当前日期 + */ + public static Date getNowDate() { + return new Date(); + } + + /** + * 获取当前日期, 默认格式为yyyy-MM-dd + * + * @return String + */ + public static String getDate() { + return dateTimeNow(YYYY_MM_DD); + } + + public static String getMills() { + return dateTimeNow(YYYY_MM_DD_HH_MM_SS_SSS); + } + + public static String getMills(Date date) { + return parseDateToStr(YYYY_MM_DD_HH_MM_SS_SSS, date); + } + + public static Long getMillsLong() { + return new Date().getTime(); + } + + public static Long getMillsLong(Date date) { + return date.getTime(); + } + + public static final String getTime() { + return dateTimeNow(YYYY_MM_DD_HH_MM_SS); + } + + public static final String dateTimeNow() { + return dateTimeNow(YYYYMMDDHHMMSS); + } + + public static final String dateTimeNow(final String format) { + return parseDateToStr(format, new Date()); + } + + public static final String dateTime(final Date date) { + return parseDateToStr(YYYY_MM_DD, date); + } + + public static final String parseDateToStr(final String format, final Date date) { + return new SimpleDateFormat(format).format(date); + } + + public static final Date dateTime(final String format, final String ts) { + try { + return new SimpleDateFormat(format).parse(ts); + } catch (ParseException e) { + throw new RuntimeException(e); + } + } + + /** + * 日期路径 即年/月/日 如2018/08/08 + */ + public static final String datePath() { + Date now = new Date(); + return DateFormatUtils.format(now, "yyyy/MM/dd"); + } + + /** + * 日期路径 即年/月/日 如20180808 + */ + public static final String dateTime() { + Date now = new Date(); + return DateFormatUtils.format(now, "yyyyMMdd"); + } + + /** + * 日期型字符串转化为日期 格式 + */ + public static Date parseDate(Object str) { + if (str == null) { + return null; + } + try { + return parseDate(str.toString(), parsePatterns); + } catch (ParseException e) { + return null; + } + } + + /** + * 获取服务器启动时间 + */ + public static Date getServerStartDate() { + long time = ManagementFactory.getRuntimeMXBean().getStartTime(); + return new Date(time); + } + + /** + * 计算两个时间差 + */ + public static String getDatePoor(Date endDate, Date nowDate) { + long nd = 1000 * 24 * 60 * 60; + long nh = 1000 * 60 * 60; + long nm = 1000 * 60; + // long ns = 1000; + // 获得两个时间的毫秒时间差异 + long diff = endDate.getTime() - nowDate.getTime(); + // 计算差多少天 + long day = diff / nd; + // 计算差多少小时 + long hour = diff % nd / nh; + // 计算差多少分钟 + long min = diff % nd % nh / nm; + // 计算差多少秒//输出结果 + // long sec = diff % nd % nh % nm / ns; + return day + "天" + hour + "小时" + min + "分钟"; + } + + public static Long strToTimeStamp(String dateTime, String format) throws ParseException { + SimpleDateFormat simpleDateFormat = new SimpleDateFormat(format); + return simpleDateFormat.parse(dateTime).getTime(); + } + + public static LocalDateTime strToLocalDateTime(String dateTimeString) throws ParseException { + // 定义日期时间格式 + DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss.SSS"); + // 使用DateTimeFormatter解析字符串为LocalDateTime + LocalDateTime dateTime = LocalDateTime.parse(dateTimeString, formatter); + return dateTime; + } + +} diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/framework/mqtt/utils/MqttDataUtils.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/framework/mqtt/utils/MqttDataUtils.java new file mode 100644 index 0000000..1113a08 --- /dev/null +++ b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/framework/mqtt/utils/MqttDataUtils.java @@ -0,0 +1,20 @@ +package cn.iocoder.yudao.module.iot.framework.mqtt.utils; + +import cn.iocoder.yudao.module.iot.framework.mqtt.entity.MqttData; +import com.alibaba.fastjson.JSON; +import lombok.extern.slf4j.Slf4j; + +@Slf4j +public class MqttDataUtils { + + public static MqttData parse(String entity) { + try { + MqttData data = JSON.parseObject(entity, MqttData.class); + return data; + } catch (Exception e) { + e.printStackTrace(); + log.error("MqttDataHandler.JSON.parseObject error:"); + } + return null; + } +} diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/framework/mqtt/utils/PubMessageUtils.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/framework/mqtt/utils/PubMessageUtils.java new file mode 100644 index 0000000..a07874c --- /dev/null +++ b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/framework/mqtt/utils/PubMessageUtils.java @@ -0,0 +1,45 @@ +package cn.iocoder.yudao.module.iot.framework.mqtt.utils; + +import lombok.extern.slf4j.Slf4j; +import org.eclipse.paho.client.mqttv3.MqttClient; +import org.eclipse.paho.client.mqttv3.MqttException; + +import java.io.UnsupportedEncodingException; +import java.nio.charset.StandardCharsets; + +/** + * 发布消息工具类 + * 注意,直接注入client pub会报错 报错原因我也不知道 如果有大佬知道就请留言告知一下 + * + * @author jie + */ +@Slf4j +public class PubMessageUtils { + + public static boolean pub(String topic, String message) throws MqttException, UnsupportedEncodingException { + return pub(topic, message.getBytes(StandardCharsets.UTF_8), 0, false); + } + + public static boolean pub(String topic, String message, int qos) throws MqttException, UnsupportedEncodingException { + return pub(topic, message.getBytes(StandardCharsets.UTF_8), qos, false); + } + + public static boolean pub(String topic, byte[] message) throws MqttException { + return pub(topic, message, 0, false); + } + + public static boolean pub(String topic, byte[] message, int qos) throws MqttException { + return pub(topic, message, qos, false); + } + + public static boolean pub(String topic, byte[] message, int qos, boolean retained) throws MqttException { + try { + MqttClient client = ApplicationContextUtil.getBean(MqttClient.class); + client.publish(topic, message, qos, retained); + } catch (MqttException e) { + log.error(e.toString()); + return false; + } + return true; + } +} diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/framework/mqtt/utils/ThreadUtils.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/framework/mqtt/utils/ThreadUtils.java new file mode 100644 index 0000000..39fe9da --- /dev/null +++ b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/framework/mqtt/utils/ThreadUtils.java @@ -0,0 +1,14 @@ +package cn.iocoder.yudao.module.iot.framework.mqtt.utils; + +import java.util.concurrent.ExecutorService; +import java.util.concurrent.Executors; + +/** + * @author jie + */ +public class ThreadUtils { + /** + * 线程池 + */ + public static ExecutorService executorService = Executors.newFixedThreadPool(50); +} diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/framework/tdengine/config/TDengineTableInitRunner.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/framework/tdengine/config/TDengineTableInitRunner.java index 1de2dcd..300c286 100644 --- a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/framework/tdengine/config/TDengineTableInitRunner.java +++ b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/framework/tdengine/config/TDengineTableInitRunner.java @@ -1,34 +1,34 @@ -package cn.iocoder.yudao.module.iot.framework.tdengine.config; - -import cn.iocoder.yudao.module.iot.service.device.message.IotDeviceMessageService; -import lombok.RequiredArgsConstructor; -import lombok.extern.slf4j.Slf4j; -import org.springframework.boot.ApplicationArguments; -import org.springframework.boot.ApplicationRunner; -import org.springframework.stereotype.Component; - -/** - * TDengine 表初始化的 Configuration - * - * @author alwayssuper - */ -@Component -@RequiredArgsConstructor -@Slf4j -public class TDengineTableInitRunner implements ApplicationRunner { - - private final IotDeviceMessageService deviceMessageService; - - @Override - public void run(ApplicationArguments args) { - try { - // 初始化设备消息表 - deviceMessageService.defineDeviceMessageStable(); - } catch (Exception ex) { - // 初始化失败时打印错误消息并退出系统 - log.error("[run][TDengine初始化设备消息表结构失败,系统无法正常运行,即将退出]", ex); - System.exit(1); - } - } - -} +//package cn.iocoder.yudao.module.iot.framework.tdengine.config; +// +//import cn.iocoder.yudao.module.iot.service.device.message.IotDeviceMessageService; +//import lombok.RequiredArgsConstructor; +//import lombok.extern.slf4j.Slf4j; +//import org.springframework.boot.ApplicationArguments; +//import org.springframework.boot.ApplicationRunner; +//import org.springframework.stereotype.Component; +// +///** +// * TDengine 表初始化的 Configuration +// * +// * @author alwayssuper +// */ +//@Component +//@RequiredArgsConstructor +//@Slf4j +//public class TDengineTableInitRunner implements ApplicationRunner { +// +// private final IotDeviceMessageService deviceMessageService; +// +// @Override +// public void run(ApplicationArguments args) { +// try { +// // 初始化设备消息表 +// deviceMessageService.defineDeviceMessageStable(); +// } catch (Exception ex) { +// // 初始化失败时打印错误消息并退出系统 +// log.error("[run][TDengine初始化设备消息表结构失败,系统无法正常运行,即将退出]", ex); +// System.exit(1); +// } +// } +// +//} diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/job/device/IotDeviceOfflineCheckJob.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/job/device/IotDeviceOfflineCheckJob.java deleted file mode 100644 index 4e0c620..0000000 --- a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/job/device/IotDeviceOfflineCheckJob.java +++ /dev/null @@ -1,73 +0,0 @@ -package cn.iocoder.yudao.module.iot.job.device; - -import cn.hutool.core.collection.CollUtil; -import cn.iocoder.yudao.framework.common.util.json.JsonUtils; -import cn.iocoder.yudao.framework.quartz.core.handler.JobHandler; -import cn.iocoder.yudao.framework.tenant.core.job.TenantJob; -import cn.iocoder.yudao.module.iot.core.enums.device.IotDeviceStateEnum; -import cn.iocoder.yudao.module.iot.core.mq.message.IotDeviceMessage; -import cn.iocoder.yudao.module.iot.dal.dataobject.device.IotDeviceDO; -import cn.iocoder.yudao.module.iot.framework.iot.config.YudaoIotProperties; -import cn.iocoder.yudao.module.iot.service.device.IotDeviceService; -import cn.iocoder.yudao.module.iot.service.device.message.IotDeviceMessageService; -import cn.iocoder.yudao.module.iot.service.device.property.IotDevicePropertyService; -import org.springframework.stereotype.Component; - -import javax.annotation.Resource; -import java.time.Duration; -import java.time.LocalDateTime; -import java.util.Collections; -import java.util.List; -import java.util.Set; - -/** - * IoT 设备离线检查 Job - * - * 检测逻辑:设备最后一条 {@link IotDeviceMessage} 消息超过一定时间,则认为设备离线 - * - * @see 阿里云 IoT —— 设备离线分析 - * @author 芋道源码 - */ -@Component -public class IotDeviceOfflineCheckJob implements JobHandler { - - @Resource - private YudaoIotProperties iotProperties; - - @Resource - private IotDeviceService deviceService; - @Resource - private IotDevicePropertyService devicePropertyService; - @Resource - private IotDeviceMessageService deviceMessageService; - - @Override - @TenantJob - public String execute(String param) { - // 1.1 获得在线设备列表 - List devices = deviceService.getDeviceListByState(IotDeviceStateEnum.ONLINE.getState()); - if (CollUtil.isEmpty(devices)) { - return JsonUtils.toJsonString(Collections.emptyList()); - } - // 1.2 获取超时的设备集合 - Set timeoutDeviceIds = devicePropertyService.getDeviceIdListByReportTime(getTimeoutTime()); - - // 2. 下线设备 - List offlineDevices = CollUtil.newArrayList(); - for (IotDeviceDO device : devices) { - if (!timeoutDeviceIds.contains(device.getId())) { - continue; - } - offlineDevices.add(new String[]{device.getProductKey(), device.getDeviceName()}); - // 为什么不直接更新状态呢?因为通过 IotDeviceMessage 可以经过一系列的处理,例如说记录日志等等 - deviceMessageService.sendDeviceMessage(IotDeviceMessage.buildStateOffline().setDeviceId(device.getId())); - } - return JsonUtils.toJsonString(offlineDevices); - } - - private LocalDateTime getTimeoutTime() { - return LocalDateTime.now().minus(Duration.ofNanos( - (long) (iotProperties.getKeepAliveTime().toNanos() * iotProperties.getKeepAliveFactor()))); - } - -} diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/job/ota/IotOtaUpgradeJob.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/job/ota/IotOtaUpgradeJob.java deleted file mode 100644 index 2285adf..0000000 --- a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/job/ota/IotOtaUpgradeJob.java +++ /dev/null @@ -1,86 +0,0 @@ -package cn.iocoder.yudao.module.iot.job.ota; - -import cn.hutool.core.collection.CollUtil; -import cn.hutool.core.util.StrUtil; -import cn.iocoder.yudao.framework.quartz.core.handler.JobHandler; -import cn.iocoder.yudao.framework.tenant.core.job.TenantJob; -import cn.iocoder.yudao.module.iot.core.enums.device.IotDeviceStateEnum; -import cn.iocoder.yudao.module.iot.dal.dataobject.device.IotDeviceDO; -import cn.iocoder.yudao.module.iot.dal.dataobject.ota.IotOtaFirmwareDO; -import cn.iocoder.yudao.module.iot.dal.dataobject.ota.IotOtaTaskRecordDO; -import cn.iocoder.yudao.module.iot.enums.ota.IotOtaTaskRecordStatusEnum; -import cn.iocoder.yudao.module.iot.service.device.IotDeviceService; -import cn.iocoder.yudao.module.iot.service.ota.IotOtaFirmwareService; -import cn.iocoder.yudao.module.iot.service.ota.IotOtaTaskRecordService; -import lombok.extern.slf4j.Slf4j; -import org.springframework.stereotype.Component; - -import javax.annotation.Resource; -import java.util.HashMap; -import java.util.List; -import java.util.Map; - -/** - * IoT OTA 升级推送 Job:查询待推送的 OTA 升级记录,并推送给设备 - * - * @author 芋道源码 - */ -@Component -@Slf4j -public class IotOtaUpgradeJob implements JobHandler { - - @Resource - private IotOtaTaskRecordService otaTaskRecordService; - @Resource - private IotOtaFirmwareService otaFirmwareService; - @Resource - private IotDeviceService deviceService; - - @Override - @TenantJob - public String execute(String param) throws Exception { - // 1. 查询待推送的 OTA 升级记录 - List records = otaTaskRecordService.getOtaRecordListByStatus( - IotOtaTaskRecordStatusEnum.PENDING.getStatus()); - if (CollUtil.isEmpty(records)) { - return null; - } - - // TODO 芋艿:可以优化成批量获取 原因是:1. N+1 问题;2. offline 的设备无需查询 - // 2. 遍历推送记录 - int successCount = 0; - int failureCount = 0; - Map otaFirmwares = new HashMap<>(); - for (IotOtaTaskRecordDO record : records) { - try { - // 2.1 设备如果不在线,直接跳过 - IotDeviceDO device = deviceService.getDeviceFromCache(record.getDeviceId()); - // TODO 芋艿:【优化】当前逻辑跳过了离线的设备,但未充分利用 MQTT 的离线消息能力。 - // 1. MQTT 协议本身支持持久化会话(Clean Session=false)和 QoS > 0 的消息,允许 broker 为离线设备缓存消息。 - // 2. 对于 OTA 升级这类非实时性强的任务,即使设备当前离线,也应该可以推送升级指令。设备在下次上线时即可收到。 - // 3. 后续可以考虑:增加一个“允许离线推送”的选项。如果开启,即使设备状态为 OFFLINE,也应尝试推送消息,依赖 MQTT Broker 的能力进行离线缓存。 - if (device == null || IotDeviceStateEnum.isNotOnline(device.getState())) { - continue; - } - // 2.2 获取 OTA 固件信息 - IotOtaFirmwareDO fireware = otaFirmwares.get(record.getFirmwareId()); - if (fireware == null) { - fireware = otaFirmwareService.getOtaFirmware(record.getFirmwareId()); - otaFirmwares.put(record.getFirmwareId(), fireware); - } - // 2.3 推送 OTA 升级任务 - boolean result = otaTaskRecordService.pushOtaTaskRecord(record, fireware, device); - if (result) { - successCount++; - } else { - failureCount++; - } - } catch (Exception e) { - failureCount++; - log.error("[execute][推送 OTA 升级任务({})发生异常]", record.getId(), e); - } - } - return StrUtil.format("升级任务推送成功:{} 条,送失败:{} 条", successCount, failureCount); - } - -} diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/job/rule/IotSceneRuleJob.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/job/rule/IotSceneRuleJob.java deleted file mode 100644 index 871623e..0000000 --- a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/job/rule/IotSceneRuleJob.java +++ /dev/null @@ -1,58 +0,0 @@ -package cn.iocoder.yudao.module.iot.job.rule; - -import cn.hutool.core.map.MapUtil; -import cn.iocoder.yudao.module.iot.enums.rule.IotSceneRuleTriggerTypeEnum; -import cn.iocoder.yudao.module.iot.service.rule.scene.IotSceneRuleService; -import lombok.extern.slf4j.Slf4j; -import org.quartz.JobExecutionContext; -import org.springframework.scheduling.quartz.QuartzJobBean; - -import javax.annotation.Resource; -import java.util.Map; - -/** - * IoT 规则场景 Job,用于执行 {@link IotSceneRuleTriggerTypeEnum#TIMER} 类型的规则场景 - * - * @author 芋道源码 - */ -@Slf4j -public class IotSceneRuleJob extends QuartzJobBean { - - /** - * JobData Key - 规则场景编号 - */ - public static final String JOB_DATA_KEY_RULE_SCENE_ID = "sceneRuleId"; - - @Resource - private IotSceneRuleService sceneRuleService; - - @Override - protected void executeInternal(JobExecutionContext context) { - // 获得规则场景编号 - Long sceneRuleId = context.getMergedJobDataMap().getLong(JOB_DATA_KEY_RULE_SCENE_ID); - - // 执行规则场景 - sceneRuleService.executeSceneRuleByTimer(sceneRuleId); - } - - /** - * 创建 JobData Map - * - * @param sceneRuleId 规则场景编号 - * @return JobData Map - */ - public static Map buildJobDataMap(Long sceneRuleId) { - return MapUtil.of(JOB_DATA_KEY_RULE_SCENE_ID, sceneRuleId); - } - - /** - * 创建 Job 名字 - * - * @param sceneRuleId 规则场景编号 - * @return Job 名字 - */ - public static String buildJobName(Long sceneRuleId) { - return String.format("%s_%d", IotSceneRuleJob.class.getSimpleName(), sceneRuleId); - } - -} diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/mq/consumer/device/IotDeviceMessageSubscriber.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/mq/consumer/device/IotDeviceMessageSubscriber.java deleted file mode 100644 index 50dec96..0000000 --- a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/mq/consumer/device/IotDeviceMessageSubscriber.java +++ /dev/null @@ -1,100 +0,0 @@ -package cn.iocoder.yudao.module.iot.mq.consumer.device; - -import cn.hutool.core.util.ObjectUtil; -import cn.iocoder.yudao.framework.tenant.core.util.TenantUtils; -import cn.iocoder.yudao.module.iot.core.enums.IotDeviceMessageMethodEnum; -import cn.iocoder.yudao.module.iot.core.enums.device.IotDeviceStateEnum; -import cn.iocoder.yudao.module.iot.core.messagebus.core.IotMessageBus; -import cn.iocoder.yudao.module.iot.core.messagebus.core.IotMessageSubscriber; -import cn.iocoder.yudao.module.iot.core.mq.message.IotDeviceMessage; -import cn.iocoder.yudao.module.iot.core.util.IotDeviceMessageUtils; -import cn.iocoder.yudao.module.iot.dal.dataobject.device.IotDeviceDO; -import cn.iocoder.yudao.module.iot.service.device.IotDeviceService; -import cn.iocoder.yudao.module.iot.service.device.message.IotDeviceMessageService; -import cn.iocoder.yudao.module.iot.service.device.property.IotDevicePropertyService; -import lombok.extern.slf4j.Slf4j; -import org.springframework.stereotype.Component; - -import javax.annotation.PostConstruct; -import javax.annotation.Resource; -import java.time.LocalDateTime; -import java.util.Objects; - -/** - * 针对 {@link IotDeviceMessage} 的业务处理器:调用 method 对应的逻辑。例如说: - * 1. {@link IotDeviceMessageMethodEnum#PROPERTY_POST} 属性上报时,记录设备属性 - * - * @author alwayssuper - */ -@Component -@Slf4j -public class IotDeviceMessageSubscriber implements IotMessageSubscriber { - - @Resource - private IotDeviceService deviceService; - @Resource - private IotDevicePropertyService devicePropertyService; - @Resource - private IotDeviceMessageService deviceMessageService; - - @Resource - private IotMessageBus messageBus; - - @PostConstruct - public void init() { - messageBus.register(this); - } - - @Override - public String getTopic() { - return IotDeviceMessage.MESSAGE_BUS_DEVICE_MESSAGE_TOPIC; - } - - @Override - public String getGroup() { - return "iot_device_message_consumer"; - } - - @Override - public void onMessage(IotDeviceMessage message) { - if (!IotDeviceMessageUtils.isUpstreamMessage(message)) { - log.error("[onMessage][message({}) 非上行消息,不进行处理]", message); - return; - } - - TenantUtils.execute(message.getTenantId(), () -> { - // 1.1 更新设备的最后时间 - IotDeviceDO device = deviceService.validateDeviceExistsFromCache(message.getDeviceId()); - devicePropertyService.updateDeviceReportTimeAsync(device.getId(), LocalDateTime.now()); - // 1.2 更新设备的连接 server - devicePropertyService.updateDeviceServerIdAsync(device.getId(), message.getServerId()); - - // 2. 未上线的设备,强制上线 - forceDeviceOnline(message, device); - - // 3. 核心:处理消息 - deviceMessageService.handleUpstreamDeviceMessage(message, device); - }); - } - - private void forceDeviceOnline(IotDeviceMessage message, IotDeviceDO device) { - // 已经在线,无需处理 - if (ObjectUtil.equal(device.getState(), IotDeviceStateEnum.ONLINE.getState())) { - return; - } - // 如果是 STATE 相关的消息,无需处理,不然就重复处理状态了 - if (Objects.equals(message.getMethod(), IotDeviceMessageMethodEnum.STATE_UPDATE.getMethod())) { - return; - } - - // 特殊:设备非在线时,主动标记设备为在线 - // 为什么不直接更新状态呢?因为通过 IotDeviceMessage 可以经过一系列的处理,例如说记录日志、规则引擎等等 - try { - deviceMessageService.sendDeviceMessage(IotDeviceMessage.buildStateUpdateOnline().setDeviceId(device.getId())); - } catch (Exception e) { - // 注意:即使执行失败,也不影响主流程 - log.error("[forceDeviceOnline][message({}) device({}) 强制设备上线失败]", message, device, e); - } - } - -} diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/mq/consumer/rule/IotDataRuleMessageSubscriber.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/mq/consumer/rule/IotDataRuleMessageSubscriber.java deleted file mode 100644 index 694c152..0000000 --- a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/mq/consumer/rule/IotDataRuleMessageSubscriber.java +++ /dev/null @@ -1,49 +0,0 @@ -package cn.iocoder.yudao.module.iot.mq.consumer.rule; - -import cn.iocoder.yudao.framework.tenant.core.util.TenantUtils; -import cn.iocoder.yudao.module.iot.core.messagebus.core.IotMessageBus; -import cn.iocoder.yudao.module.iot.core.messagebus.core.IotMessageSubscriber; -import cn.iocoder.yudao.module.iot.core.mq.message.IotDeviceMessage; -import cn.iocoder.yudao.module.iot.service.rule.data.IotDataRuleService; -import lombok.extern.slf4j.Slf4j; -import org.springframework.stereotype.Component; - -import javax.annotation.PostConstruct; -import javax.annotation.Resource; - -/** - * 针对 {@link IotDeviceMessage} 的消费者,处理数据流转 - * - * @author 芋道源码 - */ -@Component -@Slf4j -public class IotDataRuleMessageSubscriber implements IotMessageSubscriber { - - @Resource - private IotDataRuleService dataRuleService; - - @Resource - private IotMessageBus messageBus; - - @PostConstruct - public void init() { - messageBus.register(this); - } - - @Override - public String getTopic() { - return IotDeviceMessage.MESSAGE_BUS_DEVICE_MESSAGE_TOPIC; - } - - @Override - public String getGroup() { - return "iot_data_rule_consumer"; - } - - @Override - public void onMessage(IotDeviceMessage message) { - TenantUtils.execute(message.getTenantId(), () -> dataRuleService.executeDataRule(message)); - } - -} diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/mq/consumer/rule/IotSceneRuleMessageSubscriber.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/mq/consumer/rule/IotSceneRuleMessageSubscriber.java deleted file mode 100644 index b4bc4ea..0000000 --- a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/mq/consumer/rule/IotSceneRuleMessageSubscriber.java +++ /dev/null @@ -1,49 +0,0 @@ -package cn.iocoder.yudao.module.iot.mq.consumer.rule; - -import cn.iocoder.yudao.module.iot.core.messagebus.core.IotMessageBus; -import cn.iocoder.yudao.module.iot.core.messagebus.core.IotMessageSubscriber; -import cn.iocoder.yudao.module.iot.core.mq.message.IotDeviceMessage; -import cn.iocoder.yudao.module.iot.service.rule.scene.IotSceneRuleService; -import lombok.extern.slf4j.Slf4j; -import org.springframework.stereotype.Component; - -import javax.annotation.PostConstruct; -import javax.annotation.Resource; - -/** - * 针对 {@link IotDeviceMessage} 的消费者,处理规则场景 - * - * @author 芋道源码 - */ -@Component -@Slf4j -public class IotSceneRuleMessageSubscriber implements IotMessageSubscriber { - - @Resource - private IotSceneRuleService sceneRuleService; - - @Resource - private IotMessageBus messageBus; - - @PostConstruct - public void init() { - messageBus.register(this); - } - - @Override - public String getTopic() { - return IotDeviceMessage.MESSAGE_BUS_DEVICE_MESSAGE_TOPIC; - } - - @Override - public String getGroup() { - return "iot_rule_consumer"; - } - - @Override - public void onMessage(IotDeviceMessage message) { - log.info("[onMessage][消息内容({})]", message); - sceneRuleService.executeSceneRuleByDevice(message); - } - -} diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/alert/IotAlertConfigService.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/alert/IotAlertConfigService.java deleted file mode 100644 index 859aedd..0000000 --- a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/alert/IotAlertConfigService.java +++ /dev/null @@ -1,72 +0,0 @@ -package cn.iocoder.yudao.module.iot.service.alert; - -import cn.iocoder.yudao.framework.common.pojo.PageResult; -import cn.iocoder.yudao.module.iot.controller.admin.alert.vo.config.IotAlertConfigPageReqVO; -import cn.iocoder.yudao.module.iot.controller.admin.alert.vo.config.IotAlertConfigSaveReqVO; -import cn.iocoder.yudao.module.iot.dal.dataobject.alert.IotAlertConfigDO; - -import javax.validation.Valid; -import java.util.List; - -/** - * IoT 告警配置 Service 接口 - * - * @author 芋道源码 - */ -public interface IotAlertConfigService { - - /** - * 创建告警配置 - * - * @param createReqVO 创建信息 - * @return 编号 - */ - Long createAlertConfig(@Valid IotAlertConfigSaveReqVO createReqVO); - - /** - * 更新告警配置 - * - * @param updateReqVO 更新信息 - */ - void updateAlertConfig(@Valid IotAlertConfigSaveReqVO updateReqVO); - - /** - * 删除告警配置 - * - * @param id 编号 - */ - void deleteAlertConfig(Long id); - - /** - * 获得告警配置 - * - * @param id 编号 - * @return 告警配置 - */ - IotAlertConfigDO getAlertConfig(Long id); - - /** - * 获得告警配置分页 - * - * @param pageReqVO 分页查询 - * @return 告警配置分页 - */ - PageResult getAlertConfigPage(IotAlertConfigPageReqVO pageReqVO); - - /** - * 获得告警配置列表 - * - * @param status 状态 - * @return 告警配置列表 - */ - List getAlertConfigListByStatus(Integer status); - - /** - * 获得告警配置列表 - * - * @param sceneRuleId 场景流动规则编号 - * @return 告警配置列表 - */ - List getAlertConfigListBySceneRuleIdAndStatus(Long sceneRuleId, Integer status); - -} \ No newline at end of file diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/alert/IotAlertConfigServiceImpl.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/alert/IotAlertConfigServiceImpl.java deleted file mode 100644 index ea59321..0000000 --- a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/alert/IotAlertConfigServiceImpl.java +++ /dev/null @@ -1,99 +0,0 @@ -package cn.iocoder.yudao.module.iot.service.alert; - -import cn.iocoder.yudao.framework.common.pojo.PageResult; -import cn.iocoder.yudao.framework.common.util.object.BeanUtils; -import cn.iocoder.yudao.module.iot.controller.admin.alert.vo.config.IotAlertConfigPageReqVO; -import cn.iocoder.yudao.module.iot.controller.admin.alert.vo.config.IotAlertConfigSaveReqVO; -import cn.iocoder.yudao.module.iot.dal.dataobject.alert.IotAlertConfigDO; -import cn.iocoder.yudao.module.iot.dal.mysql.alert.IotAlertConfigMapper; -import cn.iocoder.yudao.module.iot.service.rule.scene.IotSceneRuleService; -import cn.iocoder.yudao.module.system.api.user.AdminUserApi; -import org.springframework.context.annotation.Lazy; -import org.springframework.stereotype.Service; -import org.springframework.validation.annotation.Validated; - -import javax.annotation.Resource; -import java.util.List; - -import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception; -import static cn.iocoder.yudao.module.iot.enums.ErrorCodeConstants.ALERT_CONFIG_NOT_EXISTS; - -/** - * IoT 告警配置 Service 实现类 - * - * @author 芋道源码 - */ -@Service -@Validated -public class IotAlertConfigServiceImpl implements IotAlertConfigService { - - @Resource - private IotAlertConfigMapper alertConfigMapper; - - @Resource - @Lazy // 延迟,避免循环依赖报错 - private IotSceneRuleService sceneRuleService; - - @Resource - private AdminUserApi adminUserApi; - - @Override - public Long createAlertConfig(IotAlertConfigSaveReqVO createReqVO) { - // 校验关联数据是否存在 - sceneRuleService.validateSceneRuleList(createReqVO.getSceneRuleIds()); - adminUserApi.validateUserList(createReqVO.getReceiveUserIds()); - - // 插入 - IotAlertConfigDO alertConfig = BeanUtils.toBean(createReqVO, IotAlertConfigDO.class); - alertConfigMapper.insert(alertConfig); - return alertConfig.getId(); - } - - @Override - public void updateAlertConfig(IotAlertConfigSaveReqVO updateReqVO) { - // 校验存在 - validateAlertConfigExists(updateReqVO.getId()); - // 校验关联数据是否存在 - sceneRuleService.validateSceneRuleList(updateReqVO.getSceneRuleIds()); - adminUserApi.validateUserList(updateReqVO.getReceiveUserIds()); - - // 更新 - IotAlertConfigDO updateObj = BeanUtils.toBean(updateReqVO, IotAlertConfigDO.class); - alertConfigMapper.updateById(updateObj); - } - - @Override - public void deleteAlertConfig(Long id) { - // 校验存在 - validateAlertConfigExists(id); - // 删除 - alertConfigMapper.deleteById(id); - } - - private void validateAlertConfigExists(Long id) { - if (alertConfigMapper.selectById(id) == null) { - throw exception(ALERT_CONFIG_NOT_EXISTS); - } - } - - @Override - public IotAlertConfigDO getAlertConfig(Long id) { - return alertConfigMapper.selectById(id); - } - - @Override - public PageResult getAlertConfigPage(IotAlertConfigPageReqVO pageReqVO) { - return alertConfigMapper.selectPage(pageReqVO); - } - - @Override - public List getAlertConfigListByStatus(Integer status) { - return alertConfigMapper.selectListByStatus(status); - } - - @Override - public List getAlertConfigListBySceneRuleIdAndStatus(Long sceneRuleId, Integer status) { - return alertConfigMapper.selectListBySceneRuleIdAndStatus(sceneRuleId, status); - } - -} \ No newline at end of file diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/alert/IotAlertRecordService.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/alert/IotAlertRecordService.java deleted file mode 100644 index 3144a31..0000000 --- a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/alert/IotAlertRecordService.java +++ /dev/null @@ -1,65 +0,0 @@ -package cn.iocoder.yudao.module.iot.service.alert; - -import cn.iocoder.yudao.framework.common.pojo.PageResult; -import cn.iocoder.yudao.module.iot.controller.admin.alert.vo.recrod.IotAlertRecordPageReqVO; -import cn.iocoder.yudao.module.iot.core.mq.message.IotDeviceMessage; -import cn.iocoder.yudao.module.iot.dal.dataobject.alert.IotAlertConfigDO; -import cn.iocoder.yudao.module.iot.dal.dataobject.alert.IotAlertRecordDO; - -import javax.validation.constraints.NotNull; -import java.util.Collection; -import java.util.List; - -/** - * IoT 告警记录 Service 接口 - * - * @author 芋道源码 - */ -public interface IotAlertRecordService { - - /** - * 获得告警记录 - * - * @param id 编号 - * @return 告警记录 - */ - IotAlertRecordDO getAlertRecord(Long id); - - /** - * 获得告警记录分页 - * - * @param pageReqVO 分页查询 - * @return 告警记录分页 - */ - PageResult getAlertRecordPage(IotAlertRecordPageReqVO pageReqVO); - - /** - * 获得指定场景规则的告警记录列表 - * - * @param sceneRuleId 场景规则编号 - * @param deviceId 设备编号 - * @param processStatus 处理状态,允许空 - * @return 告警记录列表 - */ - List getAlertRecordListBySceneRuleId(@NotNull(message = "场景规则编号不能为空") Long sceneRuleId, - Long deviceId, Boolean processStatus); - - /** - * 处理告警记录 - * - * @param ids 告警记录编号 - * @param remark 处理结果(备注) - */ - void processAlertRecordList(Collection ids, String remark); - - /** - * 创建告警记录(包含场景规则编号) - * - * @param config 告警配置 - * @param sceneRuleId 场景规则编号 - * @param deviceMessage 设备消息,可为空 - * @return 告警记录编号 - */ - Long createAlertRecord(IotAlertConfigDO config, Long sceneRuleId, IotDeviceMessage deviceMessage); - -} \ No newline at end of file diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/alert/IotAlertRecordServiceImpl.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/alert/IotAlertRecordServiceImpl.java deleted file mode 100644 index d77ec44..0000000 --- a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/alert/IotAlertRecordServiceImpl.java +++ /dev/null @@ -1,80 +0,0 @@ -package cn.iocoder.yudao.module.iot.service.alert; - -import cn.hutool.core.collection.CollUtil; -import cn.iocoder.yudao.framework.common.pojo.PageResult; -import cn.iocoder.yudao.module.iot.controller.admin.alert.vo.recrod.IotAlertRecordPageReqVO; -import cn.iocoder.yudao.module.iot.core.mq.message.IotDeviceMessage; -import cn.iocoder.yudao.module.iot.dal.dataobject.alert.IotAlertConfigDO; -import cn.iocoder.yudao.module.iot.dal.dataobject.alert.IotAlertRecordDO; -import cn.iocoder.yudao.module.iot.dal.dataobject.device.IotDeviceDO; -import cn.iocoder.yudao.module.iot.dal.mysql.alert.IotAlertRecordMapper; -import cn.iocoder.yudao.module.iot.service.device.IotDeviceService; -import org.springframework.stereotype.Service; -import org.springframework.validation.annotation.Validated; - -import javax.annotation.Resource; -import java.util.Collection; -import java.util.List; - -/** - * IoT 告警记录 Service 实现类 - * - * @author 芋道源码 - */ -@Service -@Validated -public class IotAlertRecordServiceImpl implements IotAlertRecordService { - - @Resource - private IotAlertRecordMapper alertRecordMapper; - - @Resource - private IotDeviceService deviceService; - - @Override - public IotAlertRecordDO getAlertRecord(Long id) { - return alertRecordMapper.selectById(id); - } - - @Override - public PageResult getAlertRecordPage(IotAlertRecordPageReqVO pageReqVO) { - return alertRecordMapper.selectPage(pageReqVO); - } - - @Override - public List getAlertRecordListBySceneRuleId(Long sceneRuleId, Long deviceId, Boolean processStatus) { - return alertRecordMapper.selectListBySceneRuleId(sceneRuleId, deviceId, processStatus); - } - - @Override - public void processAlertRecordList(Collection ids, String processRemark) { - if (CollUtil.isEmpty(ids)) { - return; - } - // 批量更新告警记录的处理状态 - alertRecordMapper.updateList(ids, IotAlertRecordDO.builder() - .processStatus(true).processRemark(processRemark).build()); - } - - @Override - public Long createAlertRecord(IotAlertConfigDO config, Long sceneRuleId, IotDeviceMessage message) { - // 构建告警记录 - IotAlertRecordDO.IotAlertRecordDOBuilder builder = IotAlertRecordDO.builder() - .configId(config.getId()).configName(config.getName()).configLevel(config.getLevel()) - .sceneRuleId(sceneRuleId).processStatus(false); - if (message != null) { - builder.deviceMessage(message); - // 填充设备信息 - IotDeviceDO device = deviceService.getDeviceFromCache(message.getDeviceId()); - if (device != null) { - builder.productId(device.getProductId()).deviceId(device.getId()); - } - } - - // 插入记录 - IotAlertRecordDO record = builder.build(); - alertRecordMapper.insert(record); - return record.getId(); - } - -} \ No newline at end of file diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/device/DeviceService.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/device/DeviceService.java new file mode 100644 index 0000000..68450e2 --- /dev/null +++ b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/device/DeviceService.java @@ -0,0 +1,148 @@ +package cn.iocoder.yudao.module.iot.service.device; + +import cn.hutool.core.collection.CollUtil; +import cn.iocoder.yudao.framework.common.pojo.PageParam; +import cn.iocoder.yudao.framework.common.pojo.PageResult; +import cn.iocoder.yudao.framework.common.util.collection.CollectionUtils; +import cn.iocoder.yudao.module.iot.controller.admin.device.vo.*; +import cn.iocoder.yudao.module.iot.controller.admin.devicecontactmodel.vo.DeviceContactModelPageReqVO; +import cn.iocoder.yudao.module.iot.dal.dataobject.device.DeviceAttributeDO; +import cn.iocoder.yudao.module.iot.dal.dataobject.device.DeviceDO; +import cn.iocoder.yudao.module.iot.dal.dataobject.devicecontactmodel.DeviceContactModelDO; +import com.fasterxml.jackson.core.JsonProcessingException; +import org.eclipse.paho.client.mqttv3.MqttException; + +import javax.validation.Valid; +import java.util.Collection; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +/** + * 物联设备 Service 接口 + * + * @author 内蒙必硕 + */ +public interface DeviceService { + + /** + * 创建物联设备 + * + * @param createReqVO 创建信息 + * @return 编号 + */ + DeviceDO createDevice(@Valid DeviceSaveReqVO createReqVO); + + /** + * 更新物联设备 + * + * @param updateReqVO 更新信息 + */ + void updateDevice(@Valid DeviceSaveReqVO updateReqVO); + + /** + * 删除物联设备 + * + * @param id 编号 + */ + void deleteDevice(List ids); + + /** + * 获得物联设备 + * + * @param id 编号 + * @return 物联设备 + */ + DeviceRespVO getDevice(Long id); + DeviceDO getDeviceByName(String name); + DeviceDO getDeviceByTopic(String topic); + + List getList(Collection ids); + default Map getMap(Collection ids) { + if (CollUtil.isEmpty(ids)) { + return new HashMap<>(); + } + return CollectionUtils.convertMap(getList(ids), DeviceDO::getId); + } + /** + * 获得物联设备分页 + * + * @param pageReqVO 分页查询 + * @return 物联设备分页 + */ + PageResult getDevicePage(DevicePageReqVO pageReqVO); + List selectList(DevicePageReqVO reqVO); + // ==================== 子表(设备属性) ==================== + + /** + * 获得设备属性分页 + * + * @param pageReqVO 分页查询 + * @param deviceId 设备id + * @return 设备属性分页 + */ + PageResult getDeviceAttributePage(PageParam pageReqVO, DeviceContactModelPageReqVO deviceModelAttributePageReqVO); + + /** + * 创建设备属性 + * + * @param deviceAttribute 创建信息 + * @return 编号 + */ + Long createDeviceAttribute(@Valid DeviceAttributeDO deviceAttribute); + + /** + * 更新设备属性 + * + * @param deviceAttribute 更新信息 + */ + void updateDeviceAttribute(@Valid DeviceAttributeDO deviceAttribute); + + /** + * 删除设备属性 + * + * @param id 编号 + */ + void deleteDeviceAttribute(Long id); + + /** + * 获得设备属性 + * + * @param id 编号 + * @return 设备属性 + */ + DeviceAttributeDO getDeviceAttribute(Long id); + + + Boolean connectDevice(DeviceSaveReqVO createReqVO); + + Long copyDevice(Long id); + + PageResult lineDevicePage(LineDeviceRequestVO pageReqVO); + + List lineDeviceList(LineDeviceRequestVO pageReqVO); + + Map>> singleDevice(Long deviceId) throws JsonProcessingException; + + PageResult> historyRecord(Long deviceId,String collectionStartTime, String collectionEndTime,List attributeCodes,Integer page,Integer size); + + Map> createDeviceDataMap(Long deviceId); + + List devicePointList(); + + Boolean scheduledStart(Long id); + + Boolean scheduledStop(Long id); + + DeviceOperationStatusRespVO getDeviceOperationalStatus(); + + List> getMultiDeviceAttributes(Long goviewId); + + List getDeviceAttributeList(Long deviceId); + + List deviceLedgerList(); + + void updateDeviceEnabled(@Valid DeviceUpdateEnabledReqVO updateEnabledReqVO) throws MqttException; + + DeviceDO getDeviceByMqttTopic(String topic); +} \ No newline at end of file diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/device/DeviceServiceImpl.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/device/DeviceServiceImpl.java new file mode 100644 index 0000000..adbedfc --- /dev/null +++ b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/device/DeviceServiceImpl.java @@ -0,0 +1,1599 @@ +package cn.iocoder.yudao.module.iot.service.device; + +import cn.hutool.core.collection.CollUtil; +import cn.iocoder.yudao.framework.common.enums.DeviceConnectionStatusEnum; +import cn.iocoder.yudao.framework.common.pojo.PageParam; +import cn.iocoder.yudao.framework.common.pojo.PageResult; +import cn.iocoder.yudao.framework.common.util.object.BeanUtils; +import cn.iocoder.yudao.module.iot.controller.admin.device.enums.DeviceStatusEnum; +import cn.iocoder.yudao.module.iot.controller.admin.device.scheduled.scheduler.TaskSchedulerManager; +import cn.iocoder.yudao.module.iot.controller.admin.device.scheduled.utils.CronExpressionUtils; +import cn.iocoder.yudao.module.iot.controller.admin.device.utils.DataTypeParseUtil; +import cn.iocoder.yudao.module.iot.controller.admin.device.vo.*; +import cn.iocoder.yudao.module.iot.controller.admin.devicecontactmodel.vo.DeviceContactModelPageReqVO; +import cn.iocoder.yudao.module.iot.dal.dataobject.device.DeviceAttributeDO; +import cn.iocoder.yudao.module.iot.dal.dataobject.device.DeviceDO; +import cn.iocoder.yudao.module.iot.dal.dataobject.deviceattributetype.DeviceAttributeTypeDO; +import cn.iocoder.yudao.module.iot.dal.dataobject.devicecontactmodel.DeviceContactModelDO; +import cn.iocoder.yudao.module.iot.dal.dataobject.devicemodel.DeviceModelDO; +import cn.iocoder.yudao.module.iot.dal.dataobject.devicemodelattribute.DeviceModelAttributeDO; +import cn.iocoder.yudao.module.iot.dal.dataobject.devicemodelrules.DeviceModelRulesDO; +import cn.iocoder.yudao.module.iot.dal.dataobject.deviceoperationrecord.DeviceOperationRecordDO; +import cn.iocoder.yudao.module.iot.dal.dataobject.devicepointrules.DevicePointRulesDO; +import cn.iocoder.yudao.module.iot.dal.dataobject.gateway.GatewayDO; +import cn.iocoder.yudao.module.iot.dal.mysql.device.DeviceAttributeMapper; +import cn.iocoder.yudao.module.iot.dal.mysql.device.DeviceMapper; +import cn.iocoder.yudao.module.iot.dal.mysql.deviceattributetype.DeviceAttributeTypeMapper; +import cn.iocoder.yudao.module.iot.dal.mysql.devicecontactmodel.DeviceContactModelMapper; +import cn.iocoder.yudao.module.iot.dal.mysql.devicemodel.DeviceModelMapper; +import cn.iocoder.yudao.module.iot.dal.mysql.devicemodelattribute.DeviceModelAttributeMapper; +import cn.iocoder.yudao.module.iot.dal.mysql.devicemodelrules.DeviceModelRulesMapper; +import cn.iocoder.yudao.module.iot.dal.mysql.deviceoperationrecord.DeviceOperationRecordMapper; +import cn.iocoder.yudao.module.iot.dal.mysql.devicepointrules.DevicePointRulesMapper; +import cn.iocoder.yudao.module.iot.dal.mysql.gateway.GatewayMapper; +import cn.iocoder.yudao.module.iot.framework.mqtt.consumer.IMqttservice; +import com.alibaba.fastjson.JSON; +import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; +import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper; +import com.baomidou.mybatisplus.core.toolkit.CollectionUtils; +import com.baomidou.mybatisplus.core.toolkit.Wrappers; +import com.fasterxml.jackson.core.type.TypeReference; +import com.fasterxml.jackson.databind.ObjectMapper; +import lombok.extern.slf4j.Slf4j; +import org.apache.commons.lang3.StringUtils; +import org.eclipse.paho.client.mqttv3.MqttException; +import org.springframework.beans.factory.annotation.Qualifier; +import org.springframework.jdbc.core.JdbcTemplate; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; +import org.springframework.validation.annotation.Validated; + +import javax.annotation.Resource; +import java.sql.Timestamp; +import java.text.SimpleDateFormat; +import java.time.LocalDateTime; +import java.time.format.DateTimeFormatter; +import java.util.*; +import java.util.function.Function; +import java.util.stream.Collectors; + +import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception; +import static cn.iocoder.yudao.module.iot.enums.ErrorCodeConstants.*; + +/** + * 物联设备 Service 实现类 + * + * @author 内蒙必硕 + */ +@Service +@Validated +@Slf4j +public class DeviceServiceImpl implements DeviceService { + + @Resource + private DeviceMapper deviceMapper; + @Resource + private DeviceAttributeMapper deviceAttributeMapper; + + @Resource + private DeviceModelMapper deviceModelMapper; + + + @Resource + private DeviceModelAttributeMapper deviceModelAttributeMapper; + + @Resource + private DeviceContactModelMapper deviceContactModelMapper; + +// @Resource +// private MqttDataRecordMapper mqttDataRecordMapper; + @Resource + @Qualifier("tdengineJdbcTemplate") + private JdbcTemplate tdengineJdbcTemplate; + + @Resource + private TDengineService tdengineService; + + @Resource + private DeviceAttributeTypeMapper deviceAttributeTypeMapper; + + @Resource + private TaskSchedulerManager taskSchedulerManager; + + @Resource + private DeviceModelRulesMapper deviceModelRulesMapper; + + @Resource + private DevicePointRulesMapper devicePointRulesMapper; + + @Resource + private DeviceOperationRecordMapper deviceOperationRecordMapper; + + @Resource + private IMqttservice mqttService; + +// @Resource +// private GatewayService gatewayService; + + @Resource + private GatewayMapper gatewayMapper; + + + + @Override +// @Transactional(rollbackFor = Exception.class) + public DeviceDO createDevice(DeviceSaveReqVO createReqVO) { +// if(StringUtils.isNotBlank(createReqVO.getReadTopic())){ +// DeviceDO temp = deviceMapper.selectByTopic(createReqVO.getReadTopic()); +// if (temp!=null){ +// throw exception(DEVICE_EXISTS); +// } +// } + + //判断编码是否唯一 + boolean exists = deviceMapper.exists( + Wrappers.lambdaQuery() + .eq(DeviceDO::getDeviceCode, createReqVO.getDeviceCode())); + + if (exists) { + throw exception(DEVICE_CODE_ALREADY_EXISTS); + } + + + DeviceModelDO deviceModelDO = deviceModelMapper.selectById(createReqVO.getDeviceModelId()); + + // 插入 + DeviceDO device = BeanUtils.toBean(createReqVO, DeviceDO.class); + device.setProtocol(deviceModelDO != null ? deviceModelDO.getProtocol() : ""); + //租户ID + device.setTenantId("1"); + device.setProtocol(StringUtils.isBlank(device.getProtocol()) || device.getProtocol() == null ? "MQTT":device.getProtocol()); + + deviceMapper.insert(device); + + + insertTemplatePoint(createReqVO, device); + + return device; + } + + private void insertTemplatePoint(DeviceSaveReqVO createReqVO, DeviceDO device) { + //新增模板点位 + LambdaQueryWrapper lambdaQueryWrapper = new LambdaQueryWrapper<>(); + lambdaQueryWrapper.eq(DeviceModelAttributeDO::getDeviceModelId, createReqVO.getDeviceModelId()).orderByDesc(DeviceModelAttributeDO::getId); + List deviceModelAttributeDOS = deviceModelAttributeMapper.selectList(lambdaQueryWrapper); + + List contactModelList = new ArrayList<>(); + if (!deviceModelAttributeDOS.isEmpty()) { + for (DeviceModelAttributeDO attributeDO : deviceModelAttributeDOS) { + DeviceContactModelDO contactModel = new DeviceContactModelDO(); + BeanUtils.copyProperties(attributeDO, contactModel); + contactModel.setId(null); + contactModel.setDeviceId(device.getId()); + contactModel.setCreateTime(LocalDateTime.now()); + contactModel.setUpdateTime(LocalDateTime.now()); + contactModelList.add(contactModel); + } + deviceContactModelMapper.insertBatch(contactModelList); + } + + + + //创建时序数据库 +// createTDengine(device.getId()); + + //新增规则点位 + addNewRulePoints(createReqVO.getDeviceModelId(), device.getId()); + tdengineService.createTdengineTable(device.getId(), contactModelList); + } + + private void addNewRulePoints(Long deviceModelId,Long deviceId) { + + List devicePointRulesDOList = new ArrayList<>(); + DeviceModelDO deviceModelDO = deviceModelMapper.selectById(deviceModelId); + if(deviceModelDO == null){ + return; + } + List deviceModelRulesDOList = deviceModelRulesMapper.selectList(Wrappers.lambdaQuery() + .eq(DeviceModelRulesDO::getModelId, deviceModelDO.getId())); + + for (DeviceModelRulesDO deviceModelRulesDO : deviceModelRulesDOList) { + DevicePointRulesDO devicePointRulesDO = new DevicePointRulesDO(); + BeanUtils.copyProperties(deviceModelRulesDO, devicePointRulesDO); + devicePointRulesDO.setId(null); + devicePointRulesDO.setDeviceId(deviceId); + devicePointRulesDOList.add(devicePointRulesDO); + } + devicePointRulesMapper.insertBatch(devicePointRulesDOList); + } + + + + @Override + public void updateDevice(DeviceSaveReqVO updateReqVO) { + // 校验存在 + validateDeviceExists(updateReqVO.getId()); + //校驗topic是否唯一 + validTopicExists(updateReqVO.getTopic()); + + // 更新 + DeviceDO updateObj = BeanUtils.toBean(updateReqVO, DeviceDO.class); + deviceMapper.updateById(updateObj); + } + + private void validTopicExists(String topic) { + boolean exists = deviceMapper.exists(Wrappers.lambdaQuery().eq(DeviceDO::getTopic, topic)); + if (exists){ + throw exception(DEVICE_MQTT_TOPIC_ALREADY_EXIST); + } + } + + @Override + @Transactional(rollbackFor = Exception.class) + public void deleteDevice(List ids) { + for (Long id : ids) { + // 校验存在 + validateDeviceExists(id); + //是否有引用 + validateReference(id); +// // 删除 +// deviceMapper.deleteById(id); + // 删除子表 + deleteDeviceConcatByDeviceId(id); + + } + deviceMapper.deleteByIds(ids); + } + + private void validateReference(Long deviceId) { + + int count = deviceMapper.selectIsReference(deviceId); + if (count > 0){ + throw exception(DEVICE_REFERENCES_EXIST); + } + + } + + private void deleteDeviceConcatByDeviceId(Long id) { + LambdaQueryWrapper deviceContactModelDOLambdaQueryWrapper = new LambdaQueryWrapper<>(); + deviceContactModelDOLambdaQueryWrapper.eq(DeviceContactModelDO::getDeviceId,id); + deviceContactModelMapper.delete(deviceContactModelDOLambdaQueryWrapper); + } + + private void validateDeviceExists(Long id) { + if (deviceMapper.selectById(id) == null) { + throw exception(DEVICE_NOT_EXISTS); + } + } + + @Override + public DeviceRespVO getDevice(Long id) { + DeviceDO deviceDO = deviceMapper.selectById(id); + DeviceRespVO deviceRespVO = BeanUtils.toBean(deviceDO, DeviceRespVO.class); + List devicePointRulesDOList = devicePointRulesMapper.selectList(Wrappers.lambdaQuery() + .eq(DevicePointRulesDO::getDeviceId, id)); +// +// //设置点位规则 +// if (!devicePointRulesDOList.isEmpty()){ +// +// deviceRespVO.setPointRulesVOList(devicePointRulesDOList); +// } + + return deviceRespVO; + + } + @Override + public DeviceDO getDeviceByName(String name) { + return deviceMapper.selectByName(name); + } + @Override + public DeviceDO getDeviceByTopic(String topic) { + return deviceMapper.selectByTopic(topic); + } + @Override + public PageResult getDevicePage(DevicePageReqVO pageReqVO) { + + // 1. 查询分页设备 + PageResult deviceDOPageResult = deviceMapper.selectPage(pageReqVO); + List deviceIds = deviceDOPageResult.getList().stream() + .map(DeviceDO::getId) + .collect(Collectors.toList()); + + // 2. 批量获取 TDengine 最新 ts + Map latestTsMap = tdengineService.newSelectLatestTsBatch(deviceIds); + + // 3. 批量获取最新的 DeviceOperationRecord + List ruleCodes = Arrays.stream(DeviceStatusEnum.values()) + .map(DeviceStatusEnum::getCode) + .collect(Collectors.toList()); + +// List operationRecords = deviceOperationRecordMapper.selectList( +// Wrappers.lambdaQuery() +// .in(DeviceOperationRecordDO::getDeviceId, deviceIds) +// .in(DeviceOperationRecordDO::getRule, ruleCodes) +// .orderByDesc(DeviceOperationRecordDO::getCreateTime) +// ); + + List operationRecords = tdengineService.selectLatestByDeviceAndRuleMinimal(deviceIds,ruleCodes); + + // 按 deviceId 分组,取最新一条 + Map latestRecordMap = operationRecords.stream() + .collect(Collectors.toMap( + DeviceOperationRecordDO::getDeviceId, + r -> r, + (r1, r2) -> r1.getCreateTime().isAfter(r2.getCreateTime()) ? r1 : r2 + )); + + // 4. 转换分页 DTO + PageResult deviceRespVOPageResult = BeanUtils.toBean(deviceDOPageResult, DeviceRespVO.class); + + for (DeviceRespVO deviceRespVO : deviceRespVOPageResult.getList()) { + Long deviceId = deviceRespVO.getId(); + + // 设置最新 ts + if(latestTsMap.get(deviceId) != null){ + deviceRespVO.setCollectionTime(latestTsMap.get(deviceId)); + + } + + // 设置运行状态 + DeviceOperationRecordDO record = latestRecordMap.get(deviceId); + if (record != null) { + deviceRespVO.setOperatingStatus(DeviceStatusEnum.getByCode(record.getRule()).getName()); + } else { + deviceRespVO.setOperatingStatus(DeviceStatusEnum.OFFLINE.getName()); + + } + + + } + + return deviceRespVOPageResult; + } + + @Override + public List selectList(DevicePageReqVO reqVO){ + return deviceMapper.selectList(reqVO); + } + // ==================== 子表(设备属性) ==================== + + @Override + public PageResult getDeviceAttributePage(PageParam pageReqVO, DeviceContactModelPageReqVO deviceModelAttributePageReqVO) { + // 参数校验 + if (deviceModelAttributePageReqVO.getDeviceId() == null) { + throw exception(DEVICE_ID_MODEL_NOT_EXISTS); + + } + + // 查询设备信息,只查询一次 + DeviceDO device = deviceMapper.selectById(deviceModelAttributePageReqVO.getDeviceId()); + if (device == null) { + return new PageResult<>(); + } + deviceModelAttributePageReqVO.setDeviceId(device.getId()); + // 判断设备模型ID是否有效 + PageResult deviceModelAttributeDOPageResult = deviceContactModelMapper.selectPageById(pageReqVO, deviceModelAttributePageReqVO); + + +// Map> deviceDataMap = createDeviceDataMap(device.getId()); + + //获取td数据库最新一条数据 + Map latestRow = tdengineService.newSelectLatestRow(device.getId()); + + + List records = deviceModelAttributeDOPageResult.getList(); + + if (latestRow != null && !latestRow.isEmpty()) { + + // 1. 取 ts 时间 + Object tsObj = latestRow.get("ts"); + String latestTime = null; + + if (tsObj instanceof Timestamp) { + latestTime = ((Timestamp) tsObj).toLocalDateTime().toString(); + } else if (tsObj instanceof LocalDateTime) { + latestTime = tsObj.toString(); + } else if (tsObj != null) { + latestTime = tsObj.toString(); + } + + + // 2. 遍历 records,匹配列名(attributeCode) + for (DeviceContactModelDO record : records) { + + String attributeCode = record.getAttributeCode(); + + if (attributeCode == null) { + continue; + } + + // 从 latestRow 获取对应列值 + Object rawValue = latestRow.get(attributeCode); + + if (rawValue == null) { + continue; + } + + // 根据 dataType 解析 + Object parsedValue = DataTypeParseUtil.parse(rawValue, record.getDataType()); + + // 按比例调整 + Object finalValue = adjustByRatio(parsedValue, record.getRatio()); + + // 设置值 + record.setAddressValue(finalValue); + + // 使用最新 ts 设置最新采集时间 + record.setLatestCollectionTime(latestTime); + } + + } + +// // 合并数据:将 deviceDataMap 的值赋给分页结果中的对应记录 +// List records = deviceModelAttributeDOPageResult.getList(); +// for (DeviceContactModelDO record : records) { +// Map data = deviceDataMap.get(record.getId()); +// if (data != null) { +// record.setAddressValue(adjustByRatio(data.get("addressValue"), record.getRatio())); // 设置 addressValue +// record.setLatestCollectionTime((String) data.get("timestamp")); // 设置 latestCollectionTime +// } +// } + + return deviceModelAttributeDOPageResult; + } + + private Object adjustByRatio(Object value, Double ratio) { + + if (value == null) return null; + + try { + Double result = Double.parseDouble(value.toString()); + return (ratio != null && ratio != 0.0) ? result / ratio : result; + } catch (NumberFormatException e) { + return value; // 转换失败返回原值 + } + } + + @Override + public Map> createDeviceDataMap(Long deviceId) { + // 创建结果Map:键为数据记录ID (Long),值为该条记录的详细信息 (Map) + Map> resultMap = new HashMap<>(); + + // 1. 从TDengine获取设备的最新数据记录 + Map latestDeviceData = tdengineService.getLatestDeviceData(deviceId); + if (latestDeviceData == null) { + return resultMap; // 如果没有数据,返回空Map + } + + try { + // 2. 解析queryData字段中的JSON数组,它包含多条数据记录 + String queryDataJson = (String) latestDeviceData.get("queryData"); + if (queryDataJson != null && !queryDataJson.isEmpty()) { + List dataRecords = JSON.parseArray(queryDataJson, DeviceContactModelDO.class); + + Timestamp ts = null; + String formattedTime = null; + + Object timestampObj = latestDeviceData.get("timestamp"); + if (timestampObj != null) { + if (timestampObj instanceof Timestamp) { + // 如果已经是Timestamp类型,直接转换 + ts = (Timestamp) timestampObj; + } else if (timestampObj instanceof String) { + // 如果是String类型,需要解析 + String timestampStr = (String) timestampObj; + try { + // 假设字符串是时间戳格式(如:2023-10-01 10:20:30) + ts = Timestamp.valueOf(timestampStr); + } catch (IllegalArgumentException e) { + // 如果格式不正确,尝试其他解析方式 + System.err.println("时间戳格式不正确: " + timestampStr); + // 可以设置默认值或使用当前时间 + ts = new Timestamp(System.currentTimeMillis()); + } + } + + if (ts != null) { + SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss"); + formattedTime = sdf.format(ts); + } + } + + // 4. 遍历每一条数据记录 + for (DeviceContactModelDO record : dataRecords) { + // 创建用于存储单条记录详细信息的Map + Map recordInfoMap = new HashMap<>(); + + // 4.1 放入记录的基础信息 + recordInfoMap.put("deviceId", deviceId); // 设备ID + recordInfoMap.put("timestamp", formattedTime); // 时间戳 + recordInfoMap.put("tableName", "d_" + deviceId); // 源表名 + + // 4.2 放入从JSON记录中解析出的具体数据 + recordInfoMap.put("addressValue", record.getAddressValue()); + // 可以根据需要放入其他字段,例如: + // recordInfoMap.put("address", record.getAddress()); + // recordInfoMap.put("name", record.getName()); + + // 5. 关键步骤:以数据记录自身的ID为键,将其详细信息放入结果Map + // 这里假设 DeviceContactModelDO 有一个唯一标识的id字段 + Long recordId = record.getId(); + if (recordId != null) { + resultMap.put(recordId, recordInfoMap); + } else { + // 如果记录没有ID,可以记录日志或使用其他策略(如生成临时ID),这里简单跳过 + log.error("发现一条数据记录缺少ID,已跳过"); + } + } + } + } catch (Exception e) { + // 异常处理 + log.error("处理设备" + deviceId + "的数据时发生异常: ", e.getMessage()); + return new HashMap<>(); + } + + return resultMap; + } + + @Override + public List devicePointList() { + + List devicePointRespVOList = new ArrayList<>(); + List deviceDOS = deviceMapper.selectList(Wrappers.lambdaQuery().orderByDesc(DeviceDO::getCreateTime)); + if (deviceDOS.isEmpty()){ + return devicePointRespVOList; + } + for (DeviceDO deviceDO : deviceDOS) { + DevicePointRespVO devicePointRespVO = new DevicePointRespVO(); + devicePointRespVO.setDeviceId(deviceDO.getId()); + devicePointRespVO.setDeviceName(deviceDO.getDeviceName()); + + List deviceContactModelDOS = deviceContactModelMapper.selectList( + Wrappers.lambdaQuery() + .eq(DeviceContactModelDO::getDeviceId, deviceDO.getId())); + + if (!deviceContactModelDOS.isEmpty()){ + devicePointRespVO.setContactModelDOList(deviceContactModelDOS); + } + devicePointRespVOList.add(devicePointRespVO); + + } + return devicePointRespVOList; + } + + + + @Override + public Long createDeviceAttribute(DeviceAttributeDO deviceAttribute) { + deviceAttributeMapper.insert(deviceAttribute); + return deviceAttribute.getId(); + } + + @Override + public void updateDeviceAttribute(DeviceAttributeDO deviceAttribute) { + // 校验存在 + validateDeviceAttributeExists(deviceAttribute.getId()); + // 更新 + deviceAttributeMapper.updateById(deviceAttribute); + } + + @Override + public void deleteDeviceAttribute(Long id) { + // 校验存在 + validateDeviceAttributeExists(id); + // 删除 + deviceAttributeMapper.deleteById(id); + } + + @Override + public DeviceAttributeDO getDeviceAttribute(Long id) { + return deviceAttributeMapper.selectById(id); + } + + @Override + public Boolean connectDevice(DeviceSaveReqVO createReqVO) { +// +// // 前置校验 +// DeviceDO deviceDO = validateConnectRequest(createReqVO); +// +// if (Objects.equals(createReqVO.getIsConnect(), DeviceConnectionStatusEnum.CONNECTED.getStatus())){ +// boolean connected = OpcUtils.connect(deviceDO.getId(),deviceDO.getUrl(),deviceDO.getUsername(),deviceDO.getPassword(),10); +// if (connected){ +// deviceDO.setStatus(String.valueOf(DeviceConnectionStatusEnum.CONNECTED.getStatus())); +// deviceMapper.updateById(deviceDO); +// +// String cronExpression = CronExpressionUtils.secondsToCron(deviceDO.getSampleCycle()); +// +// taskSchedulerManager.startDeviceTask( +// deviceDO.getId(), +// cronExpression +// ); +// +// +// }else { +// throw exception(OPC_CONNECT_FAILURE_DOES_NOT_EXIST); +// } +// }else if(Objects.equals(createReqVO.getIsConnect(), DeviceConnectionStatusEnum.DISCONNECTED.getStatus())){ +// boolean disconnect = OpcUtils.disconnect(deviceDO.getId()); +// if (disconnect){ +// //更新连接状态 +// deviceDO.setStatus(String.valueOf(DeviceConnectionStatusEnum.DISCONNECTED.getStatus())); +// deviceMapper.updateById(deviceDO); +// //停止定时任务 +// taskSchedulerManager.stopDeviceTask(deviceDO.getId()); +// //更新运行状态 +// updateOperationalStatus(deviceDO); +// +// +// +// }else { +// throw exception(OPC_CLOSE_CONNECT_FAILURE); +// } +// }else { +// throw exception(OPC_PARAMETER_DOES_NOT_EXIST); +// } + + return Boolean.TRUE; + } + + private void updateOperationalStatus(DeviceDO deviceDO) { + DeviceOperationRecordDO deviceOperationRecordDO = new DeviceOperationRecordDO(); + deviceOperationRecordDO.setDeviceId(deviceDO.getId()); + deviceOperationRecordDO.setRule(DeviceStatusEnum.OFFLINE.getCode()); + deviceOperationRecordMapper.insert(deviceOperationRecordDO); + } + + + + @Override + public Long copyDevice(Long id) { + if (id == null){ + throw exception(DEVICE_ID_MODEL_NOT_EXISTS); + } + DeviceDO deviceDO = deviceMapper.selectById(id); + if(deviceDO == null){ + throw exception(DEVICE_DOES_NOT_EXIST); + } + //复制实体类 + DeviceDO newDevice = new DeviceDO(); + BeanUtils.copyProperties(deviceDO, newDevice); + newDevice.setId(null); + Random random = new Random(); + int randomNumber = random.nextInt(9000) + 1000; + newDevice.setDeviceCode(deviceDO.getDeviceCode()+ "-" + randomNumber); + newDevice.setDeviceName(deviceDO.getDeviceName() + "-副本"); + deviceMapper.insert(newDevice); + + //复制关联表 + LambdaQueryWrapper lambdaQueryWrapper = new LambdaQueryWrapper(); + lambdaQueryWrapper.eq(DeviceContactModelDO::getDeviceId,deviceDO.getId()); + List deviceContactModelDOS = deviceContactModelMapper.selectList(lambdaQueryWrapper); + + + if (deviceContactModelDOS != null && !deviceContactModelDOS.isEmpty()){ + for (DeviceContactModelDO deviceModelAttributeDOS : deviceContactModelDOS) { + deviceModelAttributeDOS.setId(null); + deviceModelAttributeDOS.setDeviceId(newDevice.getId()); + } + deviceContactModelMapper.insertBatch(deviceContactModelDOS); + } + + return newDevice.getId(); + + } + + @Override + public PageResult lineDevicePage(LineDeviceRequestVO pageReqVO) { + + // 1. 查询分页设备 + // 如果有产线过滤条件 + if (StringUtils.isNotBlank(pageReqVO.getLineNode()) + || StringUtils.isNotBlank(pageReqVO.getLineName())) { + + List filteredDeviceIds = + deviceMapper.selectDeviceIdsByLine( + pageReqVO.getLineNode(), + pageReqVO.getLineName() + ); + + if (filteredDeviceIds.isEmpty()) { + return PageResult.empty(); + } + + // 把过滤后的 deviceIds 传给分页条件 + pageReqVO.setIds( filteredDeviceIds.stream() + .map(String::valueOf) + .collect(Collectors.joining(","))); + } + PageResult pageResult = + getDevicePage(BeanUtils.toBean(pageReqVO, DevicePageReqVO.class)); + + List deviceList = + Optional.ofNullable(pageResult.getList()) + .orElse(Collections.emptyList()); + + if (deviceList.isEmpty()) { + return PageResult.empty(); + } + + + + // 2. 提取 deviceIds + List deviceIds = + deviceList.stream() + .map(DeviceRespVO::getId) + .filter(Objects::nonNull) + .collect(Collectors.toList()); + + List lineList = + deviceMapper.selectLineBatch(deviceIds); + + Map lineMap = + lineList.stream() + .collect(Collectors.toMap( + LineCodeAndNameRespVO::getDeviceId, + Function.identity(), + (a, b) -> a + )); + +// // 批量查 workshop +// List> mapList = deviceMapper.selectWorkshopBatch(deviceIds); +// +// Map workshopMap = mapList.stream() +// .filter(m -> m.get("deviceId") != null) // 避免 deviceId null +// .collect(Collectors.toMap( +// m -> ((Number) m.get("deviceId")).longValue(), +// m -> m.get("workshop") != null ? m.get("workshop").toString() : "", +// (existing, replacement) -> replacement +// )); + + // 3. 批量查询最新采集时间(TDengine) + Map latestTsMap = + tdengineService.newSelectLatestTsBatch(deviceIds); + + // 4. 时间格式化器 + DateTimeFormatter formatter = + DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss"); + + // 5. 转换结果(使用 stream 更简洁) + List list = + deviceList.stream() + .map(device -> { + + LineDeviceRespVO vo = new LineDeviceRespVO(); + + vo.setId(device.getId()); + vo.setDeviceCode(device.getDeviceCode()); + vo.setDeviceName(device.getDeviceName()); + vo.setStatus(device.getStatus()); + + // 优先使用 TDengine 最新时间 + LocalDateTime latestTs = + latestTsMap.get(device.getId()); + + if (latestTs != null) { + vo.setCollectionTime(latestTs.format(formatter)); + } else if (device.getCollectionTime() != null) { + vo.setCollectionTime( + device.getCollectionTime() + .format(formatter) + ); + } else { + vo.setCollectionTime(null); + } + + // 查询产线名称 + LineCodeAndNameRespVO line = lineMap.get(device.getId()); + + if (line != null) { + vo.setLineName(line.getLineName()); + vo.setLineNode(line.getLineCode()); + } +// vo.setLineName(workshopMap.get(device.getId())); + + + return vo; + }) + .collect(Collectors.toList()); + + // 6. 返回分页结果 + return new PageResult<>(list, pageResult.getTotal()); + } + + + @Override + public List lineDeviceList(LineDeviceRequestVO pageReqVO) { + + List records = deviceMapper.lineDeviceList(pageReqVO); + if (records.isEmpty()) { + return records; + } + + // 1. 收集所有设备ID + List deviceIds = records.stream() + .map(LineDeviceRespVO::getDeviceId) + .collect(Collectors.toList()); + + // 2. 批量查询每个设备的最新时间 + Map latestTsMap = tdengineService.newSelectLatestTsBatch(deviceIds); + + // 3. 填充 collectionTime + for (LineDeviceRespVO record : records) { + LocalDateTime ts = latestTsMap.get(record.getDeviceId()); + if (ts != null) { + record.setCollectionTime(ts.toString()); // 或 formatDateTime(ts) 根据你的需求 + } + } + + return records; + + } + + @Override + public Map>> singleDevice(Long deviceId) { + + Map>> resultMap = new LinkedHashMap<>(); + + List records = deviceContactModelMapper.selectList(Wrappers.lambdaQuery() + .eq(DeviceContactModelDO::getDeviceId,deviceId) + .orderByDesc(DeviceContactModelDO::getId)); + + if (records == null || records.isEmpty()) { + return resultMap; + } + + try { + // 获取最新一行数据 + Map latestRow = tdengineService.newSelectLatestRow(deviceId); + if (latestRow == null || latestRow.isEmpty()) { + return resultMap; + } + +// // 获取 ts 时间 +// Object tsObj = latestRow.get("ts"); +// String latestTime = tsObj instanceof Timestamp ? ((Timestamp) tsObj).toLocalDateTime().toString() +// : tsObj != null ? tsObj.toString() : null; + + // 属性类型映射 + Map idToNameMap = deviceAttributeTypeMapper.selectList() + .stream() + .collect(Collectors.toMap( + DeviceAttributeTypeDO::getId, + DeviceAttributeTypeDO::getName + )); + Map attributeTypeCache = new HashMap<>(); + + // 遍历 records 填充数据 + for (DeviceContactModelDO record : records) { + + String attributeCode = record.getAttributeCode(); + if (attributeCode == null) continue; + + Object rawValue = latestRow.get(attributeCode); +// if (rawValue == null) continue; + + Object finalValue = DataTypeParseUtil.parse(rawValue, record.getDataType()); +// Object finalValue = adjustByRatio(finalValueparsedValue, record.getRatio()); + + record.setAddressValue(finalValue); +// record.setLatestCollectionTime(latestTime); + + // 属性类型名称 + String attributeTypeName = "其他"; + Object typeObj = record.getAttributeType(); + if (typeObj != null) { + String typeStr = typeObj.toString(); + try { + Long typeId = Long.parseLong(typeStr); + attributeTypeName = attributeTypeCache.computeIfAbsent(typeId, + k -> idToNameMap.getOrDefault(typeId, typeStr)); + } catch (NumberFormatException e) { + attributeTypeName = typeStr; + } + } + + Map simplifiedData = new HashMap<>(); + simplifiedData.put("addressValue", record.getAddressValue()); + simplifiedData.put("attributeName", record.getAttributeName()); + + resultMap.computeIfAbsent(attributeTypeName, k -> new ArrayList<>()).add(simplifiedData); + } + + } catch (Exception e) { + log.warn("处理设备 {} 最新数据时异常: {}", deviceId, e.getMessage()); + } + + return resultMap; + } + + + @Override + public PageResult> historyRecord( + Long deviceId, + String collectionStartTime, + String collectionEndTime, + List attributeCodes, + Integer page, + Integer pageSize) { + + List> resultList = new ArrayList<>(); + + if (deviceId == null) { + return PageResult.empty(); + } + + try { + + // 1. 查询TDengine分页数据 + List> deviceDataList = + tdengineService.newSelectBatchPage( + deviceId, + collectionStartTime, + collectionEndTime, + page, + pageSize + ); + + long total = + tdengineService.newSelectBatchTotal( + deviceId, + collectionStartTime, + collectionEndTime + ); + + if (deviceDataList.isEmpty()) { + return new PageResult<>(resultList, total); + } + + // 2. 查询属性配置(完整字段来源) + List attributeList = + deviceContactModelMapper.selectList( + Wrappers.lambdaQuery(DeviceContactModelDO.class) + .eq(DeviceContactModelDO::getDeviceId, deviceId) + .orderByAsc(DeviceContactModelDO::getSort) + ); + + // 时间格式化器 + DateTimeFormatter formatter = + DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss"); + + // 3. 遍历TDengine数据 + for (Map row : deviceDataList) { + + Object tsObj = row.get("ts"); + + if (tsObj == null) { + continue; + } + + // 格式化时间 + String collectTime; + + if (tsObj instanceof LocalDateTime) { + + collectTime = + ((LocalDateTime) tsObj) + .format(formatter); + + } else if (tsObj instanceof Timestamp) { + + collectTime = + ((Timestamp) tsObj) + .toLocalDateTime() + .format(formatter); + + } else { + + collectTime = tsObj.toString(); + } + + // 按 typeName 分组 + Map>> groupedData = + new LinkedHashMap<>(); + + // 核心逻辑:遍历 attributeList(保证全部字段返回) + for (DeviceContactModelDO attribute : attributeList) { + + // 过滤attributeCodes + if (attributeCodes != null && !attributeCodes.isEmpty() + && !attributeCodes.contains(attribute.getAttributeCode())) { + continue; + } + + + String column = + attribute.getAttributeCode(); + + Object rawValue = + row.get(column); + + // 使用 typeName 分组(核心优化点) + String typeName = + attribute.getTypeName(); + + if (typeName == null || typeName.isEmpty()) { + typeName = "其他"; + } + + Map simplifiedData = + new HashMap<>(); + + simplifiedData.put( + "addressValue", + rawValue != null + ? rawValue.toString() + : null + ); + + simplifiedData.put( + "attributeName", + attribute.getAttributeName() + ); + + groupedData + .computeIfAbsent( + typeName, + k -> new ArrayList<>() + ) + .add(simplifiedData); + } + + // 构建最终结构(保持旧结构完全一致) + Map timePointData = + new LinkedHashMap<>(groupedData); + + timePointData.put( + "collectTime", + collectTime + ); + + resultList.add(timePointData); + } + + return new PageResult<>(resultList, total); + + } catch (Exception e) { + + log.error("处理设备历史数据异常", e); + + return PageResult.empty(); + } + } + + + + + + + + private void validateDeviceAttributeExists(Long id) { + if (deviceAttributeMapper.selectById(id) == null) { + throw exception(DEVICE_ATTRIBUTE_NOT_EXISTS); + } + } + + private void deleteDeviceAttributeByDeviceId(Long deviceId) { + deviceAttributeMapper.deleteByDeviceId(deviceId); + } + + @Override + public List getList(Collection ids) { + if (CollUtil.isEmpty(ids)) { + return Collections.emptyList(); + } + return deviceMapper.selectBatchIds(ids); + } + + + + + @Override + public Boolean scheduledStart(Long id) { + try { + + // 1. 查询设备信息 + DeviceDO deviceDO = deviceMapper.selectById(id); + if (deviceDO==null) { + throw new RuntimeException("设备不存在"); + } + if (deviceDO.getSampleCycle() == null) { + throw new RuntimeException("设备cron表达式为空"); + } + + String cronExpression = CronExpressionUtils.secondsToCron(deviceDO.getSampleCycle()); + + // 2. 启动定时任务 + boolean success = taskSchedulerManager.startDeviceTask( + deviceDO.getId(), + cronExpression + ); + + if (!success) { + throw new RuntimeException("启动定时任务失败"); + } + + return true; + + } catch (Exception e) { + throw new RuntimeException("连接设备失败: " + e.getMessage(), e); + } + } + + @Override + public Boolean scheduledStop(Long id) { + try { + // 1. 停止定时任务 + taskSchedulerManager.stopDeviceTask(id); + + return true; + + } catch (Exception e) { + throw new RuntimeException("断开设备失败: " + e.getMessage(), e); + } + } + + @Override + public DeviceOperationStatusRespVO getDeviceOperationalStatus() { + + DeviceOperationStatusRespVO result = new DeviceOperationStatusRespVO(); + + // 1. 从 MySQL 获取设备总数 + Integer totalDevices = deviceMapper.getTotalDeviceCount(); + result.setTotalDevices(totalDevices); + + // 2. 从 MySQL 获取所有设备ID + List deviceIds = deviceMapper.getAllDeviceIds(); + + if (CollectionUtils.isEmpty(deviceIds)) { + // 没有设备,所有状态设为0 + result.setRunningCount(0); + result.setStandbyCount(0); + result.setFaultCount(0); + result.setWarningCount(0); + return result; + } + + // 3. 从 TDengine 批量查询设备最新状态 + Map deviceStatusMap = tdengineService.getLatestDeviceStatusAlternative(deviceIds); + + // 4. 统计各状态数量 + int runningCount = 0; + int standbyCount = 0; + int faultCount = 0; + int warningCount = 0; + + for (String status : deviceStatusMap.values()) { + switch (status) { + case "1": runningCount++; break; + case "2": standbyCount++; break; + case "3": faultCount++; break; + case "4": warningCount++; break; + default: break; + } + } + + result.setRunningCount(runningCount); + result.setStandbyCount(standbyCount); + result.setFaultCount(faultCount); + result.setWarningCount(warningCount); + + + + // 计算利用率 + calculateUtilizationRate(result); + + // 计算故障率 + calculateFaultRate(result); + + return result; + } + + + /** + * 计算利用率 + * 利用率 = 运行数量 / 设备总数 * 100% + */ + private void calculateUtilizationRate(DeviceOperationStatusRespVO statusVO) { + int totalDevices = statusVO.getTotalDevices(); + int runningCount = statusVO.getRunningCount(); + + double utilizationRate = 0.0; + if (totalDevices > 0) { + utilizationRate = (double) runningCount / totalDevices * 100; + } + + // 格式化百分比,保留2位小数 + String formattedRate = String.format("%.2f%%", utilizationRate); + statusVO.setUtilizationRate(formattedRate); + } + + /** + * 计算故障率 + * 故障率 = 故障数量 / 设备总数 * 100% + */ + private void calculateFaultRate(DeviceOperationStatusRespVO statusVO) { + int totalDevices = statusVO.getTotalDevices(); + int faultCount = statusVO.getFaultCount(); + + double faultRate = 0.0; + if (totalDevices > 0) { + faultRate = (double) faultCount / totalDevices * 100; + } + + // 格式化百分比,保留2位小数 + String formattedRate = String.format("%.2f%%", faultRate); + statusVO.setFaultRate(formattedRate); + } + + @Override + public List> getMultiDeviceAttributes(Long goviewId) { + + List> result = new ArrayList<>(); + + try { + + String deviceIdsJson = deviceMapper.selectDeviceIdsByGoviewId(goviewId); + log.info("deviceIdsJson: {}", deviceIdsJson); + if (StringUtils.isBlank(deviceIdsJson)) { + log.info("deviceIdsJson is blank, returning empty result"); + return result; + } + + // 1. 解析(允许重复 device) + List>> deviceAttributeList = + parseDeviceIdsJson(deviceIdsJson); + log.info("Parsed deviceAttributeList size: {}", deviceAttributeList.size()); + + + if (CollectionUtils.isEmpty(deviceAttributeList)) { + log.info("deviceAttributeList is empty, returning empty result"); + return result; + } + + // 2. 收集所有 deviceId / attributeId(用于批量查询) + Set allDeviceIds = deviceAttributeList.stream() + .map(Map.Entry::getKey) + .collect(Collectors.toSet()); + + Set allAttributeIds = deviceAttributeList.stream() + .flatMap(e -> e.getValue().stream()) + .collect(Collectors.toSet()); + log.info("allDeviceIds: {}", allDeviceIds); + log.info("allAttributeIds: {}", allAttributeIds); + + // 3. 批量查设备 + Map deviceMap = deviceMapper + .selectBatchIds(allDeviceIds) + .stream() + .collect(Collectors.toMap(DeviceDO::getId, Function.identity())); + log.info("deviceMap size: {}", deviceMap.size()); + + // 4. 批量查属性 + Map attributeMap = + deviceContactModelMapper.selectList( + Wrappers.lambdaQuery() + .in(DeviceContactModelDO::getId, allAttributeIds) + ).stream().collect(Collectors.toMap( + DeviceContactModelDO::getId, + Function.identity() + )); + log.info("attributeMap size: {}", attributeMap.size()); + + + // 5. 批量查运行状态 + Map latestRecordMap = + getLatestDeviceOperationRecords(allDeviceIds); + log.info("latestRecordMap size: {}", latestRecordMap.size()); + + + // 6. 批量查最新采集数据(需为批量方法) + Map>> deviceDataMap = + createDeviceDataMapBatch(allDeviceIds, attributeMap); + log.info("deviceDataMap size: {}", deviceDataMap.size()); + + + // 7. 按原始结构组装(不合并重复设备) + for (Map.Entry> entry : deviceAttributeList) { + + Long deviceId = entry.getKey(); + Set attributeIds = entry.getValue(); + log.info("Processing deviceId: {}, attributeIds: {}", deviceId, attributeIds); + + DeviceDO device = deviceMap.get(deviceId); + if (device == null) { + log.info("Device not found for deviceId: {}", deviceId); + + continue; + } + + Map deviceResult = new HashMap<>(); + deviceResult.put("deviceId", deviceId); + deviceResult.put("deviceName", device.getDeviceName()); + deviceResult.put("deviceCode", device.getDeviceCode()); + + String operatingStatus = + getDeviceOperatingStatus(deviceId, latestRecordMap); + deviceResult.put("operatingStatus", operatingStatus); + + List> attributeList = new ArrayList<>(); + + Map> deviceAttrData = + deviceDataMap.getOrDefault(deviceId, Collections.emptyMap()); + + for (Long attributeId : attributeIds) { + + DeviceContactModelDO attribute = attributeMap.get(attributeId); + if (attribute == null) { + log.info("Attribute not found for attributeId: {}", attributeId); + continue; + } + + Map attrResult = new HashMap<>(); + attrResult.put("attributeId", attribute.getId()); + attrResult.put("attributeName", attribute.getAttributeName()); + attrResult.put("attributeCode", attribute.getAttributeCode()); + attrResult.put("dataType", attribute.getDataType()); + attrResult.put("dataUnit", attribute.getDataUnit()); + + Map latestData = deviceAttrData.get(attributeId); + if (latestData != null) { + attrResult.put("addressValue", + adjustByRatio(latestData.get("addressValue"), attribute.getRatio())); + attrResult.put("latestCollectionTime", latestData.get("timestamp")); + } + + attributeList.add(attrResult); + } + + deviceResult.put("attributes", attributeList); + result.add(deviceResult); + } + log.info("Final result size: {}", result.size()); + } catch (Exception e) { + log.error("Failed to get multi-device attributes by goviewId: {}", goviewId, e); + } + + return result; + } + + private Map>> createDeviceDataMapBatch(Set allDeviceIds, Map deviceContactModelDOMap) { + return tdengineService.createDeviceDataMapBatch(allDeviceIds,deviceContactModelDOMap); + } + + + /** + * 获取设备运行状态 + * @param deviceId 设备ID + * @param latestRecordMap 设备ID到最新运行记录的映射 + * @return 设备运行状态名称 + */ + private String getDeviceOperatingStatus(Long deviceId, Map latestRecordMap) { + DeviceOperationRecordDO record = latestRecordMap.get(deviceId); + if (record != null) { + // 根据运行记录的rule字段获取设备状态 + DeviceStatusEnum statusEnum = DeviceStatusEnum.getByCode(record.getRule()); + return statusEnum != null ? statusEnum.getName() : DeviceStatusEnum.OFFLINE.getName(); + } else { + // 如果没有运行记录,默认为离线状态 + return DeviceStatusEnum.OFFLINE.getName(); + } + } + + /** + * 批量获取设备的最新运行记录 + * @param deviceIds 设备ID集合 + * @return 设备ID到最新运行记录的映射 + */ + private Map getLatestDeviceOperationRecords(Set deviceIds) { + if (deviceIds.isEmpty()) { + return Collections.emptyMap(); + } + + List records = deviceOperationRecordMapper.selectLatestRecords(deviceIds); + + // 直接用 Stream 转 Map + return records.stream() + .collect(Collectors.toMap( + DeviceOperationRecordDO::getDeviceId, + Function.identity() + )); + } + + /** + * 解析JSON格式的deviceIds字符串 + * @param deviceIdsJson JSON格式的设备和属性映射字符串 + * @return 设备ID到属性ID集合的映射(已去重) + */ + private List>> parseDeviceIdsJson(String deviceIdsJson) { + + List>> result = new ArrayList<>(); + + if (StringUtils.isBlank(deviceIdsJson)) { + return result; + } + + try { + ObjectMapper objectMapper = new ObjectMapper(); + + List> deviceAttributeList = + objectMapper.readValue(deviceIdsJson, + new TypeReference>>() {}); + + for (Map item : deviceAttributeList) { + + Object deviceIdObj = item.get("deviceId"); + if (deviceIdObj == null) { + continue; + } + + Long deviceId; + if (deviceIdObj instanceof Number) { + deviceId = ((Number) deviceIdObj).longValue(); + } else { + deviceId = Long.parseLong(deviceIdObj.toString()); + } + + Set attributeIds = new HashSet<>(); + + Object attributesIdsObj = item.get("attributesIds"); + if (attributesIdsObj instanceof List) { + for (Object attrIdObj : (List) attributesIdsObj) { + if (attrIdObj instanceof Number) { + attributeIds.add(((Number) attrIdObj).longValue()); + } else if (attrIdObj != null) { + try { + attributeIds.add(Long.parseLong(attrIdObj.toString())); + } catch (Exception ignore) { + } + } + } + } + + // 每条记录单独保存,不合并 + result.add(new AbstractMap.SimpleEntry<>(deviceId, attributeIds)); + } + + } catch (Exception e) { + log.error("Error parsing deviceIds JSON: {}", deviceIdsJson, e); + } + + return result; + } + + @Override + public List getDeviceAttributeList(Long deviceId) { + return deviceContactModelMapper.selectList(Wrappers.lambdaQuery() + .eq(DeviceContactModelDO::getDeviceId,deviceId) + .orderByDesc(DeviceContactModelDO::getId)); + } + + @Override + public void updateDeviceEnabled(DeviceUpdateEnabledReqVO updateEnabledReqVO) throws MqttException { + + // 1. 校验任务是否存在 + DeviceDO deviceDO = deviceMapper.selectById(updateEnabledReqVO.getId()); + if (deviceDO == null) { + throw exception(DEVICE_NOT_EXISTS); + } + if (StringUtils.isBlank(deviceDO.getTopic())) { + throw exception(DEVICE_MQTT_TOPIC_EXIST); + } + + //TODO 待优化 + if (!"MQTT".equals(deviceDO.getProtocol())) { + throw exception(DEVICE_MQTT_TOPIC_EXIST); + } + + // 2. 如果状态没有变化,直接返回 + if (Objects.equals(deviceDO.getIsEnable(), updateEnabledReqVO.getEnabled())) { + return; + } + + // 3. 执行状态更新操作 + executeEnableUpdate(deviceDO, updateEnabledReqVO.getEnabled()); + + + + } + + @Override + public DeviceDO getDeviceByMqttTopic(String topic) { + return deviceMapper.selectOne(Wrappers.lambdaQuery().eq(DeviceDO::getTopic,topic)); + } + + private void executeEnableUpdate(DeviceDO deviceDO, Boolean enabled) { + + try { + // 1. 更新设备启用状态 + deviceDO.setIsEnable(enabled); + deviceMapper.updateById(deviceDO); + + String topic = deviceDO.getTopic(); + + // 2. 启用设备 + if (enabled) { + + // 2.1 MQTT订阅 + try { + mqttService.subscribeTopic(topic); + log.info("MQTT订阅成功: {}", topic); + } catch (MqttException e) { + log.error("MQTT订阅失败: {}", topic, e); + } + + // 2.2 保存到iot_gateway表-用于重启后自动订阅 + GatewayDO gateway = gatewayMapper.selectOne( + new LambdaQueryWrapper() + .eq(GatewayDO::getTopic, topic) + .last("LIMIT 1") + ); + + if (gateway == null) { + + gateway = new GatewayDO(); + gateway.setGatewayName(deviceDO.getDeviceName()); + gateway.setGatewayCode(deviceDO.getDeviceCode()); + gateway.setTopic(topic); + gateway.setIsEnable(true); + gateway.setDeleted(false); + + gatewayMapper.insert(gateway); + + log.info("新增gateway订阅记录成功 topic={}", topic); + + } else { + //更新gateway状态 + gateway.setIsEnable(true); + gateway.setUpdateTime(LocalDateTime.now()); + + gatewayMapper.updateById(gateway); + log.info("更新gateway启用状态 topic={}", topic); + + } + + } + + // 3. 禁用设备 + else { + + // 3.1 MQTT取消订阅 + try { + mqttService.unsubscribeTopic(topic); + log.info("MQTT取消订阅成功: {}", topic); + } catch (MqttException e) { + log.error("MQTT取消订阅失败: {}", topic, e); + } + + // 3.2 更新gateway状态为禁用 + gatewayMapper.update( + null, + new LambdaUpdateWrapper() + .eq(GatewayDO::getTopic, topic) + .set(GatewayDO::getIsEnable, false) + .set(GatewayDO::getUpdateTime, LocalDateTime.now()) + ); + + log.info("gateway订阅记录已禁用 topic={}", topic); + + //更新设备运行状态为离线 +// updateOperationalStatus(deviceDO); + log.info("更新设备运行状态为离线 deviceId={}", deviceDO.getId()); + + + } + + } catch (Exception e) { + + log.error("更新设备状态失败 deviceCode={}", deviceDO.getDeviceCode(), e); + + } + } + + + + @Override + public List deviceLedgerList() { + return deviceMapper.deviceLedgerList(); + } + +} \ No newline at end of file diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/device/IotDeviceGroupService.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/device/IotDeviceGroupService.java deleted file mode 100644 index e2d441d..0000000 --- a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/device/IotDeviceGroupService.java +++ /dev/null @@ -1,97 +0,0 @@ -package cn.iocoder.yudao.module.iot.service.device; - -import cn.hutool.core.collection.CollUtil; -import cn.hutool.core.collection.ListUtil; -import cn.iocoder.yudao.framework.common.pojo.PageResult; -import cn.iocoder.yudao.module.iot.controller.admin.device.vo.group.IotDeviceGroupPageReqVO; -import cn.iocoder.yudao.module.iot.controller.admin.device.vo.group.IotDeviceGroupSaveReqVO; -import cn.iocoder.yudao.module.iot.dal.dataobject.device.IotDeviceGroupDO; - -import javax.validation.Valid; -import java.util.Collection; -import java.util.List; - -import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertList; - -/** - * IoT 设备分组 Service 接口 - * - * @author 芋道源码 - */ -public interface IotDeviceGroupService { - - /** - * 创建设备分组 - * - * @param createReqVO 创建信息 - * @return 编号 - */ - Long createDeviceGroup(@Valid IotDeviceGroupSaveReqVO createReqVO); - - /** - * 更新设备分组 - * - * @param updateReqVO 更新信息 - */ - void updateDeviceGroup(@Valid IotDeviceGroupSaveReqVO updateReqVO); - - /** - * 删除设备分组 - * - * @param id 编号 - */ - void deleteDeviceGroup(Long id); - - /** - * 校验设备分组是否存在 - * - * @param ids 设备分组 ID 数组 - */ - default List validateDeviceGroupExists(Collection ids) { - if (CollUtil.isEmpty(ids)) { - return ListUtil.empty(); - } - return convertList(ids, this::validateDeviceGroupExists); - } - - /** - * 校验设备分组是否存在 - * - * @param id 设备分组 ID - * @return 设备分组 - */ - IotDeviceGroupDO validateDeviceGroupExists(Long id); - - /** - * 获得设备分组 - * - * @param id 编号 - * @return 设备分组 - */ - IotDeviceGroupDO getDeviceGroup(Long id); - - /** - * 获得设备分组 - * - * @param name 名称 - * @return 设备分组 - */ - IotDeviceGroupDO getDeviceGroupByName(String name); - - /** - * 获得设备分组分页 - * - * @param pageReqVO 分页查询 - * @return 设备分组分页 - */ - PageResult getDeviceGroupPage(IotDeviceGroupPageReqVO pageReqVO); - - /** - * 获得设备分组列表 - * - * @param status 状态 - * @return 设备分组列表 - */ - List getDeviceGroupListByStatus(Integer status); - -} \ No newline at end of file diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/device/IotDeviceGroupServiceImpl.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/device/IotDeviceGroupServiceImpl.java deleted file mode 100644 index 6b92cbf..0000000 --- a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/device/IotDeviceGroupServiceImpl.java +++ /dev/null @@ -1,94 +0,0 @@ -package cn.iocoder.yudao.module.iot.service.device; - -import cn.iocoder.yudao.framework.common.pojo.PageResult; -import cn.iocoder.yudao.framework.common.util.object.BeanUtils; -import cn.iocoder.yudao.module.iot.controller.admin.device.vo.group.IotDeviceGroupPageReqVO; -import cn.iocoder.yudao.module.iot.controller.admin.device.vo.group.IotDeviceGroupSaveReqVO; -import cn.iocoder.yudao.module.iot.dal.dataobject.device.IotDeviceGroupDO; -import cn.iocoder.yudao.module.iot.dal.mysql.device.IotDeviceGroupMapper; -import org.springframework.stereotype.Service; -import org.springframework.validation.annotation.Validated; - -import javax.annotation.Resource; -import java.util.List; - -import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception; -import static cn.iocoder.yudao.module.iot.enums.ErrorCodeConstants.DEVICE_GROUP_DELETE_FAIL_DEVICE_EXISTS; -import static cn.iocoder.yudao.module.iot.enums.ErrorCodeConstants.DEVICE_GROUP_NOT_EXISTS; - -/** - * IoT 设备分组 Service 实现类 - * - * @author 芋道源码 - */ -@Service -@Validated -public class IotDeviceGroupServiceImpl implements IotDeviceGroupService { - - @Resource - private IotDeviceGroupMapper deviceGroupMapper; - - @Resource - private IotDeviceService deviceService; - - @Override - public Long createDeviceGroup(IotDeviceGroupSaveReqVO createReqVO) { - // 插入 - IotDeviceGroupDO deviceGroup = BeanUtils.toBean(createReqVO, IotDeviceGroupDO.class); - deviceGroupMapper.insert(deviceGroup); - // 返回 - return deviceGroup.getId(); - } - - @Override - public void updateDeviceGroup(IotDeviceGroupSaveReqVO updateReqVO) { - // 校验存在 - validateDeviceGroupExists(updateReqVO.getId()); - // 更新 - IotDeviceGroupDO updateObj = BeanUtils.toBean(updateReqVO, IotDeviceGroupDO.class); - deviceGroupMapper.updateById(updateObj); - } - - @Override - public void deleteDeviceGroup(Long id) { - // 1.1 校验存在 - validateDeviceGroupExists(id); - // 1.2 校验是否存在设备 - if (deviceService.getDeviceCountByGroupId(id) > 0) { - throw exception(DEVICE_GROUP_DELETE_FAIL_DEVICE_EXISTS); - } - - // 删除 - deviceGroupMapper.deleteById(id); - } - - @Override - public IotDeviceGroupDO validateDeviceGroupExists(Long id) { - IotDeviceGroupDO group = deviceGroupMapper.selectById(id); - if (group == null) { - throw exception(DEVICE_GROUP_NOT_EXISTS); - } - return group; - } - - @Override - public IotDeviceGroupDO getDeviceGroup(Long id) { - return deviceGroupMapper.selectById(id); - } - - @Override - public IotDeviceGroupDO getDeviceGroupByName(String name) { - return deviceGroupMapper.selectByName(name); - } - - @Override - public PageResult getDeviceGroupPage(IotDeviceGroupPageReqVO pageReqVO) { - return deviceGroupMapper.selectPage(pageReqVO); - } - - @Override - public List getDeviceGroupListByStatus(Integer status) { - return deviceGroupMapper.selectListByStatus(status); - } - -} \ No newline at end of file diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/device/IotDeviceModbusConfigService.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/device/IotDeviceModbusConfigService.java deleted file mode 100644 index a18f623..0000000 --- a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/device/IotDeviceModbusConfigService.java +++ /dev/null @@ -1,48 +0,0 @@ -package cn.iocoder.yudao.module.iot.service.device; - -import cn.iocoder.yudao.module.iot.controller.admin.device.vo.modbus.IotDeviceModbusConfigSaveReqVO; -import cn.iocoder.yudao.module.iot.core.biz.dto.IotModbusDeviceConfigListReqDTO; -import cn.iocoder.yudao.module.iot.dal.dataobject.device.IotDeviceModbusConfigDO; - -import javax.validation.Valid; -import java.util.List; - -/** - * IoT 设备 Modbus 连接配置 Service 接口 - * - * @author 芋道源码 - */ -public interface IotDeviceModbusConfigService { - - /** - * 保存设备 Modbus 连接配置(新增或更新) - * - * @param saveReqVO 保存信息 - */ - void saveDeviceModbusConfig(@Valid IotDeviceModbusConfigSaveReqVO saveReqVO); - - /** - * 获得设备 Modbus 连接配置 - * - * @param id 编号 - * @return 设备 Modbus 连接配置 - */ - IotDeviceModbusConfigDO getDeviceModbusConfig(Long id); - - /** - * 根据设备编号获得 Modbus 连接配置 - * - * @param deviceId 设备编号 - * @return 设备 Modbus 连接配置 - */ - IotDeviceModbusConfigDO getDeviceModbusConfigByDeviceId(Long deviceId); - - /** - * 获得 Modbus 连接配置列表 - * - * @param listReqDTO 查询参数 - * @return Modbus 连接配置列表 - */ - List getDeviceModbusConfigList(IotModbusDeviceConfigListReqDTO listReqDTO); - -} diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/device/IotDeviceModbusConfigServiceImpl.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/device/IotDeviceModbusConfigServiceImpl.java deleted file mode 100644 index e1117dc..0000000 --- a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/device/IotDeviceModbusConfigServiceImpl.java +++ /dev/null @@ -1,89 +0,0 @@ -package cn.iocoder.yudao.module.iot.service.device; - -import cn.hutool.core.lang.Assert; -import cn.hutool.core.util.StrUtil; -import cn.iocoder.yudao.framework.common.util.object.BeanUtils; -import cn.iocoder.yudao.module.iot.controller.admin.device.vo.modbus.IotDeviceModbusConfigSaveReqVO; -import cn.iocoder.yudao.module.iot.core.biz.dto.IotModbusDeviceConfigListReqDTO; -import cn.iocoder.yudao.module.iot.core.enums.IotProtocolTypeEnum; -import cn.iocoder.yudao.module.iot.dal.dataobject.device.IotDeviceDO; -import cn.iocoder.yudao.module.iot.dal.dataobject.device.IotDeviceModbusConfigDO; -import cn.iocoder.yudao.module.iot.dal.dataobject.product.IotProductDO; -import cn.iocoder.yudao.module.iot.dal.mysql.device.IotDeviceModbusConfigMapper; -import cn.iocoder.yudao.module.iot.service.product.IotProductService; -import org.springframework.stereotype.Service; -import org.springframework.validation.annotation.Validated; - -import javax.annotation.Resource; -import java.util.List; - -/** - * IoT 设备 Modbus 连接配置 Service 实现类 - * - * @author 芋道源码 - */ -@Service -@Validated -public class IotDeviceModbusConfigServiceImpl implements IotDeviceModbusConfigService { - - @Resource - private IotDeviceModbusConfigMapper modbusConfigMapper; - - @Resource - private IotDeviceService deviceService; - @Resource - private IotProductService productService; - - @Override - public void saveDeviceModbusConfig(IotDeviceModbusConfigSaveReqVO saveReqVO) { - // 1.1 校验设备存在 - IotDeviceDO device = deviceService.validateDeviceExists(saveReqVO.getDeviceId()); - // 1.2 根据产品 protocolType 条件校验 - IotProductDO product = productService.getProduct(device.getProductId()); - Assert.notNull(product, "产品不存在"); - validateModbusConfigByProtocolType(saveReqVO, product.getProtocolType()); - - // 2. 根据数据库中是否已有配置,决定是新增还是更新 - IotDeviceModbusConfigDO existConfig = modbusConfigMapper.selectByDeviceId(saveReqVO.getDeviceId()); - if (existConfig == null) { - IotDeviceModbusConfigDO modbusConfig = BeanUtils.toBean(saveReqVO, IotDeviceModbusConfigDO.class); - modbusConfigMapper.insert(modbusConfig); - } else { - IotDeviceModbusConfigDO updateObj = BeanUtils.toBean(saveReqVO, IotDeviceModbusConfigDO.class, - o -> o.setId(existConfig.getId())); - modbusConfigMapper.updateById(updateObj); - } - } - - @Override - public IotDeviceModbusConfigDO getDeviceModbusConfig(Long id) { - return modbusConfigMapper.selectById(id); - } - - @Override - public IotDeviceModbusConfigDO getDeviceModbusConfigByDeviceId(Long deviceId) { - return modbusConfigMapper.selectByDeviceId(deviceId); - } - - @Override - public List getDeviceModbusConfigList(IotModbusDeviceConfigListReqDTO listReqDTO) { - return modbusConfigMapper.selectList(listReqDTO); - } - - private void validateModbusConfigByProtocolType(IotDeviceModbusConfigSaveReqVO saveReqVO, String protocolType) { - IotProtocolTypeEnum protocolTypeEnum = IotProtocolTypeEnum.of(protocolType); - if (protocolTypeEnum == null) { - return; - } - if (protocolTypeEnum == IotProtocolTypeEnum.MODBUS_TCP_CLIENT) { - Assert.isTrue(StrUtil.isNotEmpty(saveReqVO.getIp()), "Client 模式下,IP 地址不能为空"); - Assert.notNull(saveReqVO.getPort(), "Client 模式下,端口不能为空"); - Assert.notNull(saveReqVO.getTimeout(), "Client 模式下,连接超时时间不能为空"); - Assert.notNull(saveReqVO.getRetryInterval(), "Client 模式下,重试间隔不能为空"); - } else if (protocolTypeEnum == IotProtocolTypeEnum.MODBUS_TCP_SERVER) { - Assert.notNull(saveReqVO.getMode(), "Server 模式下,工作模式不能为空"); - Assert.notNull(saveReqVO.getFrameFormat(), "Server 模式下,数据帧格式不能为空"); - } - } - -} diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/device/IotDeviceModbusPointService.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/device/IotDeviceModbusPointService.java deleted file mode 100644 index fa06a1d..0000000 --- a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/device/IotDeviceModbusPointService.java +++ /dev/null @@ -1,75 +0,0 @@ -package cn.iocoder.yudao.module.iot.service.device; - -import cn.iocoder.yudao.framework.common.pojo.PageResult; -import cn.iocoder.yudao.module.iot.controller.admin.device.vo.modbus.IotDeviceModbusPointPageReqVO; -import cn.iocoder.yudao.module.iot.controller.admin.device.vo.modbus.IotDeviceModbusPointSaveReqVO; -import cn.iocoder.yudao.module.iot.dal.dataobject.device.IotDeviceModbusPointDO; - -import javax.validation.Valid; -import java.util.Collection; -import java.util.List; -import java.util.Map; - -/** - * IoT 设备 Modbus 点位配置 Service 接口 - * - * @author 芋道源码 - */ -public interface IotDeviceModbusPointService { - - /** - * 创建设备 Modbus 点位配置 - * - * @param createReqVO 创建信息 - * @return 编号 - */ - Long createDeviceModbusPoint(@Valid IotDeviceModbusPointSaveReqVO createReqVO); - - /** - * 更新设备 Modbus 点位配置 - * - * @param updateReqVO 更新信息 - */ - void updateDeviceModbusPoint(@Valid IotDeviceModbusPointSaveReqVO updateReqVO); - - /** - * 删除设备 Modbus 点位配置 - * - * @param id 编号 - */ - void deleteDeviceModbusPoint(Long id); - - /** - * 获得设备 Modbus 点位配置 - * - * @param id 编号 - * @return 设备 Modbus 点位配置 - */ - IotDeviceModbusPointDO getDeviceModbusPoint(Long id); - - /** - * 获得设备 Modbus 点位配置分页 - * - * @param pageReqVO 分页查询 - * @return 设备 Modbus 点位配置分页 - */ - PageResult getDeviceModbusPointPage(IotDeviceModbusPointPageReqVO pageReqVO); - - /** - * 物模型变更时,更新关联点位的冗余字段(identifier、name) - * - * @param thingModelId 物模型编号 - * @param identifier 物模型标识符 - * @param name 物模型名称 - */ - void updateDeviceModbusPointByThingModel(Long thingModelId, String identifier, String name); - - /** - * 根据设备编号批量获得启用的点位配置 Map - * - * @param deviceIds 设备编号集合 - * @return 设备点位 Map,key 为设备编号,value 为点位配置列表 - */ - Map> getEnabledDeviceModbusPointMapByDeviceIds(Collection deviceIds); - -} diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/device/IotDeviceModbusPointServiceImpl.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/device/IotDeviceModbusPointServiceImpl.java deleted file mode 100644 index 3669196..0000000 --- a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/device/IotDeviceModbusPointServiceImpl.java +++ /dev/null @@ -1,135 +0,0 @@ -package cn.iocoder.yudao.module.iot.service.device; - -import cn.hutool.core.collection.CollUtil; -import cn.hutool.core.util.ObjUtil; -import cn.iocoder.yudao.framework.common.enums.CommonStatusEnum; -import cn.iocoder.yudao.framework.common.pojo.PageResult; -import cn.iocoder.yudao.framework.common.util.object.BeanUtils; -import cn.iocoder.yudao.module.iot.controller.admin.device.vo.modbus.IotDeviceModbusPointPageReqVO; -import cn.iocoder.yudao.module.iot.controller.admin.device.vo.modbus.IotDeviceModbusPointSaveReqVO; -import cn.iocoder.yudao.module.iot.dal.dataobject.device.IotDeviceModbusPointDO; -import cn.iocoder.yudao.module.iot.dal.dataobject.thingmodel.IotThingModelDO; -import cn.iocoder.yudao.module.iot.dal.mysql.device.IotDeviceModbusPointMapper; -import cn.iocoder.yudao.module.iot.service.thingmodel.IotThingModelService; -import org.springframework.stereotype.Service; -import org.springframework.validation.annotation.Validated; - -import javax.annotation.Resource; -import java.util.Collection; -import java.util.Collections; -import java.util.List; -import java.util.Map; - -import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception; -import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertMultiMap; -import static cn.iocoder.yudao.module.iot.enums.ErrorCodeConstants.*; - -/** - * IoT 设备 Modbus 点位配置 Service 实现类 - * - * @author 芋道源码 - */ -@Service -@Validated -public class IotDeviceModbusPointServiceImpl implements IotDeviceModbusPointService { - - @Resource - private IotDeviceModbusPointMapper modbusPointMapper; - - @Resource - private IotDeviceService deviceService; - - @Resource - private IotThingModelService thingModelService; - - @Override - public Long createDeviceModbusPoint(IotDeviceModbusPointSaveReqVO createReqVO) { - // 1.1 校验设备存在 - deviceService.validateDeviceExists(createReqVO.getDeviceId()); - // 1.2 校验物模型属性存在 - IotThingModelDO thingModel = validateThingModelExists(createReqVO.getThingModelId()); - // 1.3 校验同一设备下点位唯一性(基于 identifier) - validateDeviceModbusPointUnique(createReqVO.getDeviceId(), thingModel.getIdentifier(), null); - - // 2. 插入 - IotDeviceModbusPointDO modbusPoint = BeanUtils.toBean(createReqVO, IotDeviceModbusPointDO.class, - o -> o.setIdentifier(thingModel.getIdentifier()).setName(thingModel.getName())); - modbusPointMapper.insert(modbusPoint); - return modbusPoint.getId(); - } - - @Override - public void updateDeviceModbusPoint(IotDeviceModbusPointSaveReqVO updateReqVO) { - // 1.1 校验存在 - validateDeviceModbusPointExists(updateReqVO.getId()); - // 1.2 校验设备存在 - deviceService.validateDeviceExists(updateReqVO.getDeviceId()); - // 1.3 校验物模型属性存在 - IotThingModelDO thingModel = validateThingModelExists(updateReqVO.getThingModelId()); - // 1.4 校验同一设备下点位唯一性 - validateDeviceModbusPointUnique(updateReqVO.getDeviceId(), thingModel.getIdentifier(), updateReqVO.getId()); - - // 2. 更新 - IotDeviceModbusPointDO updateObj = BeanUtils.toBean(updateReqVO, IotDeviceModbusPointDO.class, - o -> o.setIdentifier(thingModel.getIdentifier()).setName(thingModel.getName())); - modbusPointMapper.updateById(updateObj); - } - - @Override - public void updateDeviceModbusPointByThingModel(Long thingModelId, String identifier, String name) { - IotDeviceModbusPointDO updateObj = new IotDeviceModbusPointDO() - .setIdentifier(identifier).setName(name); - modbusPointMapper.updateByThingModelId(thingModelId, updateObj); - } - - private IotThingModelDO validateThingModelExists(Long id) { - IotThingModelDO thingModel = thingModelService.getThingModel(id); - if (thingModel == null) { - throw exception(THING_MODEL_NOT_EXISTS); - } - return thingModel; - } - - @Override - public void deleteDeviceModbusPoint(Long id) { - // 校验存在 - validateDeviceModbusPointExists(id); - // 删除 - modbusPointMapper.deleteById(id); - } - - private void validateDeviceModbusPointExists(Long id) { - IotDeviceModbusPointDO point = modbusPointMapper.selectById(id); - if (point == null) { - throw exception(DEVICE_MODBUS_POINT_NOT_EXISTS); - } - } - - private void validateDeviceModbusPointUnique(Long deviceId, String identifier, Long excludeId) { - IotDeviceModbusPointDO point = modbusPointMapper.selectByDeviceIdAndIdentifier(deviceId, identifier); - if (point != null && ObjUtil.notEqual(point.getId(), excludeId)) { - throw exception(DEVICE_MODBUS_POINT_EXISTS); - } - } - - @Override - public IotDeviceModbusPointDO getDeviceModbusPoint(Long id) { - return modbusPointMapper.selectById(id); - } - - @Override - public PageResult getDeviceModbusPointPage(IotDeviceModbusPointPageReqVO pageReqVO) { - return modbusPointMapper.selectPage(pageReqVO); - } - - @Override - public Map> getEnabledDeviceModbusPointMapByDeviceIds(Collection deviceIds) { - if (CollUtil.isEmpty(deviceIds)) { - return Collections.emptyMap(); - } - List pointList = modbusPointMapper.selectListByDeviceIdsAndStatus( - deviceIds, CommonStatusEnum.ENABLE.getStatus()); - return convertMultiMap(pointList, IotDeviceModbusPointDO::getDeviceId); - } - -} diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/device/IotDeviceService.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/device/IotDeviceService.java deleted file mode 100644 index ae92a6e..0000000 --- a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/device/IotDeviceService.java +++ /dev/null @@ -1,378 +0,0 @@ -package cn.iocoder.yudao.module.iot.service.device; - -import cn.iocoder.yudao.framework.common.pojo.PageResult; -import cn.iocoder.yudao.module.iot.controller.admin.device.vo.device.*; -import cn.iocoder.yudao.module.iot.core.biz.dto.IotDeviceAuthReqDTO; -import cn.iocoder.yudao.module.iot.core.biz.dto.IotSubDeviceRegisterFullReqDTO; -import cn.iocoder.yudao.module.iot.core.enums.device.IotDeviceStateEnum; -import cn.iocoder.yudao.module.iot.core.mq.message.IotDeviceMessage; -import cn.iocoder.yudao.module.iot.core.topic.IotDeviceIdentity; -import cn.iocoder.yudao.module.iot.core.topic.auth.IotDeviceRegisterReqDTO; -import cn.iocoder.yudao.module.iot.core.topic.auth.IotDeviceRegisterRespDTO; -import cn.iocoder.yudao.module.iot.core.topic.auth.IotSubDeviceRegisterRespDTO; -import cn.iocoder.yudao.module.iot.core.topic.topo.IotDeviceTopoGetRespDTO; -import cn.iocoder.yudao.module.iot.dal.dataobject.device.IotDeviceDO; - -import javax.annotation.Nullable; -import javax.validation.Valid; -import java.math.BigDecimal; -import java.time.LocalDateTime; -import java.util.Collection; -import java.util.List; -import java.util.Map; - -import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertMap; - -/** - * IoT 设备 Service 接口 - * - * @author 芋道源码 - */ -public interface IotDeviceService { - - /** - * 【管理员】创建设备 - * - * @param createReqVO 创建信息 - * @return 编号 - */ - Long createDevice(@Valid IotDeviceSaveReqVO createReqVO); - - /** - * 更新设备 - * - * @param updateReqVO 更新信息 - */ - void updateDevice(@Valid IotDeviceSaveReqVO updateReqVO); - - /** - * 更新设备状态 - * - * @param device 设备信息 - * @param state 状态 - */ - void updateDeviceState(IotDeviceDO device, Integer state); - - /** - * 更新设备状态 - * - * @param id 编号 - * @param state 状态 - */ - void updateDeviceState(Long id, Integer state); - - /** - * 更新设备分组 - * - * @param updateReqVO 更新信息 - */ - void updateDeviceGroup(@Valid IotDeviceUpdateGroupReqVO updateReqVO); - - /** - * 删除单个设备 - * - * @param id 编号 - */ - void deleteDevice(Long id); - - /** - * 删除多个设备 - * - * @param ids 编号数组 - */ - void deleteDeviceList(Collection ids); - - /** - * 校验设备是否存在 - * - * @param id 设备 ID - * @return 设备对象 - */ - IotDeviceDO validateDeviceExists(Long id); - - /** - * 【缓存】校验设备是否存在 - * - * @param id 设备 ID - * @return 设备对象 - */ - IotDeviceDO validateDeviceExistsFromCache(Long id); - - /** - * 获得设备 - * - * @param id 编号 - * @return IoT 设备 - */ - IotDeviceDO getDevice(Long id); - - /** - * 【缓存】获得设备信息 - *

- * 注意:该方法会忽略租户信息,所以调用时,需要确认会不会有跨租户访问的风险!!! - * - * @param id 编号 - * @return IoT 设备 - */ - IotDeviceDO getDeviceFromCache(Long id); - - /** - * 【缓存】根据产品 key 和设备名称,获得设备信息 - *

- * 注意:该方法会忽略租户信息,所以调用时,需要确认会不会有跨租户访问的风险!!! - * - * @param productKey 产品 key - * @param deviceName 设备名称 - * @return 设备信息 - */ - IotDeviceDO getDeviceFromCache(String productKey, String deviceName); - - /** - * 获得设备分页 - * - * @param pageReqVO 分页查询 - * @return IoT 设备分页 - */ - PageResult getDevicePage(IotDevicePageReqVO pageReqVO); - - /** - * 根据条件,获得设备列表 - * - * @param deviceType 设备类型 - * @param productId 产品编号 - * @return 设备列表 - */ - List getDeviceListByCondition(@Nullable Integer deviceType, - @Nullable Long productId); - - /** - * 获得状态,获得设备列表 - * - * @param state 状态 - * @return 设备列表 - */ - List getDeviceListByState(Integer state); - - /** - * 根据产品编号,获取设备列表 - * - * @param productId 产品编号 - * @return 设备列表 - */ - List getDeviceListByProductId(Long productId); - - /** - * 基于产品编号,获得设备数量 - * - * @param productId 产品编号 - * @return 设备数量 - */ - Long getDeviceCountByProductId(Long productId); - - /** - * 获得设备数量 - * - * @param groupId 分组编号 - * @return 设备数量 - */ - Long getDeviceCountByGroupId(Long groupId); - - /** - * 导入设备 - * - * @param importDevices 导入设备列表 - * @param updateSupport 是否支持更新 - * @return 导入结果 - */ - IotDeviceImportRespVO importDevice(List importDevices, boolean updateSupport); - - /** - * 获得设备数量 - * - * @param createTime 创建时间,如果为空,则统计所有设备数量 - * @return 设备数量 - */ - Long getDeviceCount(@Nullable LocalDateTime createTime); - - /** - * 获得设备认证信息 - * - * @param id 设备编号 - * @return MQTT 连接参数 - */ - IotDeviceAuthInfoRespVO getDeviceAuthInfo(Long id); - - /** - * 获得各个产品下的设备数量 Map - * - * @return key: 产品编号, value: 设备数量 - */ - Map getDeviceCountMapByProductId(); - - /** - * 获得各个状态下的设备数量 Map - * - * @return key: 设备状态枚举 {@link IotDeviceStateEnum} - * value: 设备数量 - */ - Map getDeviceCountMapByState(); - - /** - * 通过产品标识和设备名称列表获取设备列表 - * - * @param productKey 产品标识 - * @param deviceNames 设备名称列表 - * @return 设备列表 - */ - List getDeviceListByProductKeyAndNames(String productKey, List deviceNames); - - /** - * 认证设备 - * - * @param authReqDTO 认证信息 - * @return 是否认证成功 - */ - boolean authDevice(@Valid IotDeviceAuthReqDTO authReqDTO); - - /** - * 校验设备是否存在 - * - * @param ids 设备编号数组 - */ - List validateDeviceListExists(Collection ids); - - /** - * 获得设备列表 - * - * @param ids 设备编号数组 - * @return 设备列表 - */ - List getDeviceList(Collection ids); - - /** - * 获得设备 Map - * - * @param ids 设备编号数组 - * @return 设备 Map - */ - default Map getDeviceMap(Collection ids) { - return convertMap(getDeviceList(ids), IotDeviceDO::getId); - } - - /** - * 更新设备固件版本 - * - * @param deviceId 设备编号 - * @param firmwareId 固件编号 - */ - void updateDeviceFirmware(Long deviceId, Long firmwareId); - - /** - * 更新设备定位信息(GeoLocation 上报时调用) - * - * @param device 设备信息(用于清除缓存) - * @param longitude 经度 - * @param latitude 纬度 - */ - void updateDeviceLocation(IotDeviceDO device, BigDecimal longitude, BigDecimal latitude); - - /** - * 获得有位置信息的设备列表 - * - * @return 设备列表 - */ - List getDeviceListByHasLocation(); - - // ========== 网关-拓扑管理(后台操作) ========== - - /** - * 绑定子设备到网关 - * - * @param subIds 子设备编号列表 - * @param gatewayId 网关设备编号 - */ - void bindDeviceGateway(Collection subIds, Long gatewayId); - - /** - * 解绑子设备与网关 - * - * @param subIds 子设备编号列表 - * @param gatewayId 网关设备编号 - */ - void unbindDeviceGateway(Collection subIds, Long gatewayId); - - /** - * 获取未绑定网关的子设备分页 - * - * @param pageReqVO 分页查询参数(仅使用 productId、deviceName、nickname) - * @return 子设备分页 - */ - PageResult getUnboundSubDevicePage(IotDevicePageReqVO pageReqVO); - - /** - * 根据网关编号获取子设备列表 - * - * @param gatewayId 网关设备编号 - * @return 子设备列表 - */ - List getDeviceListByGatewayId(Long gatewayId); - - // ========== 网关-拓扑管理(设备上报) ========== - - /** - * 处理添加拓扑关系消息(网关设备上报) - * - * @param message 消息 - * @param gatewayDevice 网关设备 - * @return 成功添加的子设备列表 - */ - List handleTopoAddMessage(IotDeviceMessage message, IotDeviceDO gatewayDevice); - - /** - * 处理删除拓扑关系消息(网关设备上报) - * - * @param message 消息 - * @param gatewayDevice 网关设备 - * @return 成功删除的子设备列表 - */ - List handleTopoDeleteMessage(IotDeviceMessage message, IotDeviceDO gatewayDevice); - - /** - * 处理获取拓扑关系消息(网关设备上报) - * - * @param gatewayDevice 网关设备 - * @return 拓扑关系响应 - */ - IotDeviceTopoGetRespDTO handleTopoGetMessage(IotDeviceDO gatewayDevice); - - // ========== 设备动态注册 ========== - - /** - * 直连/网关设备动态注册 - * - * @param reqDTO 动态注册请求 - * @return 注册结果(包含 DeviceSecret) - */ - IotDeviceRegisterRespDTO registerDevice(@Valid IotDeviceRegisterReqDTO reqDTO); - - /** - * 网关子设备动态注册 - *

- * 与 {@link #handleSubDeviceRegisterMessage} 方法的区别: - * 该方法网关设备信息通过 reqDTO 参数传入,而 {@link #handleSubDeviceRegisterMessage} 方法通过 gatewayDevice 参数传入 - * - * @param reqDTO 子设备注册请求(包含网关设备信息) - * @return 注册结果列表 - */ - List registerSubDevices(@Valid IotSubDeviceRegisterFullReqDTO reqDTO); - - /** - * 处理子设备动态注册消息(网关设备上报) - * - * @param message 消息 - * @param gatewayDevice 网关设备 - * @return 注册结果列表 - */ - List handleSubDeviceRegisterMessage(IotDeviceMessage message, IotDeviceDO gatewayDevice); - -} diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/device/IotDeviceServiceImpl.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/device/IotDeviceServiceImpl.java deleted file mode 100644 index a0712d5..0000000 --- a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/device/IotDeviceServiceImpl.java +++ /dev/null @@ -1,932 +0,0 @@ -package cn.iocoder.yudao.module.iot.service.device; - -import cn.hutool.core.collection.CollUtil; -import cn.hutool.core.util.BooleanUtil; -import cn.hutool.core.util.IdUtil; -import cn.hutool.core.util.ObjUtil; -import cn.hutool.core.util.StrUtil; -import cn.hutool.extra.spring.SpringUtil; -import cn.iocoder.yudao.framework.common.exception.ServiceException; -import cn.iocoder.yudao.framework.common.pojo.PageResult; -import cn.iocoder.yudao.framework.common.util.json.JsonUtils; -import cn.iocoder.yudao.framework.common.util.object.BeanUtils; -import cn.iocoder.yudao.framework.common.util.validation.ValidationUtils; -import cn.iocoder.yudao.framework.tenant.core.aop.TenantIgnore; -import cn.iocoder.yudao.framework.tenant.core.util.TenantUtils; -import cn.iocoder.yudao.module.iot.controller.admin.device.vo.device.*; -import cn.iocoder.yudao.module.iot.core.biz.dto.IotDeviceAuthReqDTO; -import cn.iocoder.yudao.module.iot.core.biz.dto.IotSubDeviceRegisterFullReqDTO; -import cn.iocoder.yudao.module.iot.core.enums.IotDeviceMessageMethodEnum; -import cn.iocoder.yudao.module.iot.core.enums.device.IotDeviceStateEnum; -import cn.iocoder.yudao.module.iot.core.mq.message.IotDeviceMessage; -import cn.iocoder.yudao.module.iot.core.topic.IotDeviceIdentity; -import cn.iocoder.yudao.module.iot.core.topic.auth.IotDeviceRegisterReqDTO; -import cn.iocoder.yudao.module.iot.core.topic.auth.IotDeviceRegisterRespDTO; -import cn.iocoder.yudao.module.iot.core.topic.auth.IotSubDeviceRegisterReqDTO; -import cn.iocoder.yudao.module.iot.core.topic.auth.IotSubDeviceRegisterRespDTO; -import cn.iocoder.yudao.module.iot.core.topic.topo.IotDeviceTopoAddReqDTO; -import cn.iocoder.yudao.module.iot.core.topic.topo.IotDeviceTopoChangeReqDTO; -import cn.iocoder.yudao.module.iot.core.topic.topo.IotDeviceTopoDeleteReqDTO; -import cn.iocoder.yudao.module.iot.core.topic.topo.IotDeviceTopoGetRespDTO; -import cn.iocoder.yudao.module.iot.core.util.IotDeviceAuthUtils; -import cn.iocoder.yudao.module.iot.core.util.IotProductAuthUtils; -import cn.iocoder.yudao.module.iot.dal.dataobject.device.IotDeviceDO; -import cn.iocoder.yudao.module.iot.dal.dataobject.device.IotDeviceGroupDO; -import cn.iocoder.yudao.module.iot.dal.dataobject.product.IotProductDO; -import cn.iocoder.yudao.module.iot.dal.mysql.device.IotDeviceMapper; -import cn.iocoder.yudao.module.iot.dal.redis.RedisKeyConstants; -import cn.iocoder.yudao.module.iot.enums.product.IotProductDeviceTypeEnum; -import cn.iocoder.yudao.module.iot.service.device.message.IotDeviceMessageService; -import cn.iocoder.yudao.module.iot.service.product.IotProductService; -import lombok.extern.slf4j.Slf4j; -import org.springframework.cache.annotation.CacheEvict; -import org.springframework.cache.annotation.Cacheable; -import org.springframework.cache.annotation.Caching; -import org.springframework.context.annotation.Lazy; -import org.springframework.stereotype.Service; -import org.springframework.transaction.annotation.Transactional; -import org.springframework.validation.annotation.Validated; - -import javax.annotation.Nullable; -import javax.annotation.Resource; -import javax.validation.ConstraintViolationException; -import java.math.BigDecimal; -import java.time.LocalDateTime; -import java.util.*; - -import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception; -import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertList; -import static cn.iocoder.yudao.module.iot.enums.ErrorCodeConstants.*; -import static java.util.Collections.singletonList; - -/** - * IoT 设备 Service 实现类 - * - * @author 芋道源码 - */ -@Service -@Validated -@Slf4j -public class IotDeviceServiceImpl implements IotDeviceService { - - @Resource - private IotDeviceMapper deviceMapper; - - @Resource - @Lazy // 延迟加载,解决循环依赖 - private IotProductService productService; - @Resource - @Lazy // 延迟加载,解决循环依赖 - private IotDeviceGroupService deviceGroupService; - @Resource - @Lazy // 延迟加载,解决循环依赖 - private IotDeviceMessageService deviceMessageService; - - private IotDeviceServiceImpl getSelf() { - return SpringUtil.getBean(getClass()); - } - - @Override - public Long createDevice(IotDeviceSaveReqVO createReqVO) { - return createDevice0(createReqVO).getId(); - } - - private IotDeviceDO createDevice0(IotDeviceSaveReqVO createReqVO) { - // 1.1 校验产品是否存在 - IotProductDO product = productService.getProduct(createReqVO.getProductId()); - if (product == null) { - throw exception(PRODUCT_NOT_EXISTS); - } - // 1.2 统一校验 - validateCreateDeviceParam(product.getProductKey(), createReqVO.getDeviceName(), - createReqVO.getGatewayId(), product); - // 1.3 校验分组存在 - deviceGroupService.validateDeviceGroupExists(createReqVO.getGroupIds()); - // 1.4 校验设备序列号全局唯一 - validateSerialNumberUnique(createReqVO.getSerialNumber(), null); - - // 2. 插入到数据库 - IotDeviceDO device = BeanUtils.toBean(createReqVO, IotDeviceDO.class); - initDevice(device, product); - deviceMapper.insert(device); - return device; - } - - private void validateCreateDeviceParam(String productKey, String deviceName, - Long gatewayId, IotProductDO product) { - // 校验设备名称在同一产品下是否唯一 - TenantUtils.executeIgnore(() -> { - if (deviceMapper.selectByProductKeyAndDeviceName(productKey, deviceName) != null) { - throw exception(DEVICE_NAME_EXISTS); - } - }); - // 校验父设备是否为合法网关 - if (IotProductDeviceTypeEnum.isGatewaySub(product.getDeviceType()) - && gatewayId != null) { - validateGatewayDeviceExists(gatewayId); - } - } - - /** - * 校验设备序列号全局唯一性 - * - * @param serialNumber 设备序列号 - * @param excludeId 排除的设备编号(用于更新时排除自身) - */ - private void validateSerialNumberUnique(String serialNumber, Long excludeId) { - if (StrUtil.isBlank(serialNumber)) { - return; - } - IotDeviceDO existDevice = deviceMapper.selectBySerialNumber(serialNumber); - if (existDevice != null && ObjUtil.notEqual(existDevice.getId(), excludeId)) { - throw exception(DEVICE_SERIAL_NUMBER_EXISTS); - } - } - - private void initDevice(IotDeviceDO device, IotProductDO product) { - device.setProductId(product.getId()).setProductKey(product.getProductKey()) - .setDeviceType(product.getDeviceType()) - .setDeviceSecret(generateDeviceSecret()) // 生成密钥 - .setState(IotDeviceStateEnum.INACTIVE.getState()); // 默认未激活 - } - - private String generateDeviceSecret() { - return IdUtil.fastSimpleUUID(); - } - - @Override - public void updateDevice(IotDeviceSaveReqVO updateReqVO) { - updateReqVO.setDeviceName(null).setProductId(null); // 不允许更新 - // 1.1 校验存在 - IotDeviceDO device = validateDeviceExists(updateReqVO.getId()); - // 1.2 校验父设备是否为合法网关 - if (IotProductDeviceTypeEnum.isGatewaySub(device.getDeviceType()) - && updateReqVO.getGatewayId() != null) { - validateGatewayDeviceExists(updateReqVO.getGatewayId()); - } - // 1.3 校验分组存在 - deviceGroupService.validateDeviceGroupExists(updateReqVO.getGroupIds()); - // 1.4 校验设备序列号全局唯一 - validateSerialNumberUnique(updateReqVO.getSerialNumber(), updateReqVO.getId()); - - // 2. 更新到数据库 - IotDeviceDO updateObj = BeanUtils.toBean(updateReqVO, IotDeviceDO.class); - deviceMapper.updateById(updateObj); - - // 3. 清空对应缓存 - deleteDeviceCache(device); - } - - @Override - @Transactional(rollbackFor = Exception.class) - public void updateDeviceGroup(IotDeviceUpdateGroupReqVO updateReqVO) { - // 1.1 校验设备存在 - List devices = deviceMapper.selectByIds(updateReqVO.getIds()); - if (CollUtil.isEmpty(devices)) { - return; - } - // 1.2 校验分组存在 - deviceGroupService.validateDeviceGroupExists(updateReqVO.getGroupIds()); - - // 3. 更新设备分组 - deviceMapper.updateBatch(convertList(devices, device -> new IotDeviceDO() - .setId(device.getId()).setGroupIds(updateReqVO.getGroupIds()))); - - // 4. 清空对应缓存 - deleteDeviceCache(devices); - } - - @Override - public void deleteDevice(Long id) { - // 1.1 校验存在 - IotDeviceDO device = validateDeviceExists(id); - // 1.2 如果是网关设备,检查是否有子设备绑定 - if (IotProductDeviceTypeEnum.isGateway(device.getDeviceType()) - && deviceMapper.selectCountByGatewayId(id) > 0) { - throw exception(DEVICE_GATEWAY_HAS_SUB); - } - - // 2. 删除设备 - deviceMapper.deleteById(id); - - // 3. 清空对应缓存 - deleteDeviceCache(device); - } - - @Override - @Transactional(rollbackFor = Exception.class) - public void deleteDeviceList(Collection ids) { - // 1.1 校验存在 - if (CollUtil.isEmpty(ids)) { - return; - } - List devices = deviceMapper.selectByIds(ids); - if (CollUtil.isEmpty(devices)) { - return; - } - // 1.2 如果是网关设备,检查是否有子设备绑定 - for (IotDeviceDO device : devices) { - if (IotProductDeviceTypeEnum.isGateway(device.getDeviceType()) - && deviceMapper.selectCountByGatewayId(device.getId()) > 0) { - throw exception(DEVICE_GATEWAY_HAS_SUB); - } - } - - // 2. 删除设备 - deviceMapper.deleteByIds(ids); - - // 3. 清空对应缓存 - deleteDeviceCache(devices); - } - - @Override - public IotDeviceDO validateDeviceExists(Long id) { - IotDeviceDO device = deviceMapper.selectById(id); - if (device == null) { - throw exception(DEVICE_NOT_EXISTS); - } - return device; - } - - @Override - public IotDeviceDO validateDeviceExistsFromCache(Long id) { - IotDeviceDO device = getSelf().getDeviceFromCache(id); - if (device == null) { - throw exception(DEVICE_NOT_EXISTS); - } - return device; - } - - /** - * 校验网关设备是否存在 - * - * @param id 设备 ID - */ - private void validateGatewayDeviceExists(Long id) { - IotDeviceDO device = deviceMapper.selectById(id); - if (device == null) { - throw exception(DEVICE_GATEWAY_NOT_EXISTS); - } - if (!IotProductDeviceTypeEnum.isGateway(device.getDeviceType())) { - throw exception(DEVICE_NOT_GATEWAY); - } - } - - @Override - public IotDeviceDO getDevice(Long id) { - return deviceMapper.selectById(id); - } - - @Override - @Cacheable(value = RedisKeyConstants.DEVICE, key = "#id", unless = "#result == null") - @TenantIgnore // 忽略租户信息 - public IotDeviceDO getDeviceFromCache(Long id) { - return deviceMapper.selectById(id); - } - - @Override - @Cacheable(value = RedisKeyConstants.DEVICE, key = "#productKey + '_' + #deviceName", unless = "#result == null") - @TenantIgnore // 忽略租户信息,跨租户 productKey + deviceName 是唯一的 - public IotDeviceDO getDeviceFromCache(String productKey, String deviceName) { - return deviceMapper.selectByProductKeyAndDeviceName(productKey, deviceName); - } - - @Override - public PageResult getDevicePage(IotDevicePageReqVO pageReqVO) { - return deviceMapper.selectPage(pageReqVO); - } - - @Override - public List getDeviceListByCondition(@Nullable Integer deviceType, @Nullable Long productId) { - return deviceMapper.selectListByCondition(deviceType, productId); - } - - @Override - public List getDeviceListByState(Integer state) { - return deviceMapper.selectListByState(state); - } - - @Override - public List getDeviceListByProductId(Long productId) { - return deviceMapper.selectListByProductId(productId); - } - - @Override - public void updateDeviceState(IotDeviceDO device, Integer state) { - // 1. 更新状态和时间 - IotDeviceDO updateObj = new IotDeviceDO().setId(device.getId()).setState(state); - if (device.getOnlineTime() == null - && Objects.equals(state, IotDeviceStateEnum.ONLINE.getState())) { - updateObj.setActiveTime(LocalDateTime.now()); - } - if (Objects.equals(state, IotDeviceStateEnum.ONLINE.getState())) { - updateObj.setOnlineTime(LocalDateTime.now()); - } else if (Objects.equals(state, IotDeviceStateEnum.OFFLINE.getState())) { - updateObj.setOfflineTime(LocalDateTime.now()); - } - deviceMapper.updateById(updateObj); - - // 2. 清空对应缓存 - deleteDeviceCache(device); - - // 3. 网关设备下线时,联动所有子设备下线 - if (Objects.equals(state, IotDeviceStateEnum.OFFLINE.getState()) - && IotProductDeviceTypeEnum.isGateway(device.getDeviceType())) { - handleGatewayOffline(device); - } - } - - /** - * 处理网关下线,联动所有子设备下线 - * - * @param gatewayDevice 网关设备 - */ - private void handleGatewayOffline(IotDeviceDO gatewayDevice) { - List subDevices = deviceMapper.selectListByGatewayId(gatewayDevice.getId()); - if (CollUtil.isEmpty(subDevices)) { - return; - } - for (IotDeviceDO subDevice : subDevices) { - if (Objects.equals(subDevice.getState(), IotDeviceStateEnum.ONLINE.getState())) { - try { - updateDeviceState(subDevice, IotDeviceStateEnum.OFFLINE.getState()); - log.info("[handleGatewayOffline][网关({}/{}) 下线,子设备({}/{}) 联动下线]", - gatewayDevice.getProductKey(), gatewayDevice.getDeviceName(), - subDevice.getProductKey(), subDevice.getDeviceName()); - } catch (Exception ex) { - log.error("[handleGatewayOffline][子设备({}/{}) 下线失败]", - subDevice.getProductKey(), subDevice.getDeviceName(), ex); - } - } - } - } - - @Override - public void updateDeviceState(Long id, Integer state) { - // 校验存在 - IotDeviceDO device = validateDeviceExists(id); - // 执行更新 - updateDeviceState(device, state); - } - - @Override - public Long getDeviceCountByProductId(Long productId) { - return deviceMapper.selectCountByProductId(productId); - } - - @Override - public Long getDeviceCountByGroupId(Long groupId) { - return deviceMapper.selectCountByGroupId(groupId); - } - - @Override - @Transactional(rollbackFor = Exception.class) // 添加事务,异常则回滚所有导入 - public IotDeviceImportRespVO importDevice(List importDevices, boolean updateSupport) { - // 1. 参数校验 - if (CollUtil.isEmpty(importDevices)) { - throw exception(DEVICE_IMPORT_LIST_IS_EMPTY); - } - - // 2. 遍历,逐个创建 or 更新 - IotDeviceImportRespVO respVO = IotDeviceImportRespVO.builder().createDeviceNames(new ArrayList<>()) - .updateDeviceNames(new ArrayList<>()).failureDeviceNames(new LinkedHashMap<>()).build(); - importDevices.forEach(importDevice -> { - try { - // 2.1.1 校验字段是否符合要求 - try { - ValidationUtils.validate(importDevice); - } catch (ConstraintViolationException ex) { - respVO.getFailureDeviceNames().put(importDevice.getDeviceName(), ex.getMessage()); - return; - } - // 2.1.2 校验产品是否存在 - IotProductDO product = productService.validateProductExists(importDevice.getProductKey()); - // 2.1.3 校验父设备是否存在 - Long gatewayId = null; - if (StrUtil.isNotEmpty(importDevice.getParentDeviceName())) { - IotDeviceDO gatewayDevice = deviceMapper.selectByDeviceName(importDevice.getParentDeviceName()); - if (gatewayDevice == null) { - throw exception(DEVICE_GATEWAY_NOT_EXISTS); - } - if (!IotProductDeviceTypeEnum.isGateway(gatewayDevice.getDeviceType())) { - throw exception(DEVICE_NOT_GATEWAY); - } - gatewayId = gatewayDevice.getId(); - } - // 2.1.4 校验设备分组是否存在 - Set groupIds = new HashSet<>(); - if (StrUtil.isNotEmpty(importDevice.getGroupNames())) { - String[] groupNames = importDevice.getGroupNames().split(","); - for (String groupName : groupNames) { - IotDeviceGroupDO group = deviceGroupService.getDeviceGroupByName(groupName); - if (group == null) { - throw exception(DEVICE_GROUP_NOT_EXISTS); - } - groupIds.add(group.getId()); - } - } - - // 2.2.1 判断如果不存在,在进行插入 - IotDeviceDO existDevice = deviceMapper.selectByDeviceName(importDevice.getDeviceName()); - if (existDevice == null) { - createDevice(new IotDeviceSaveReqVO() - .setDeviceName(importDevice.getDeviceName()) - .setProductId(product.getId()).setGatewayId(gatewayId).setGroupIds(groupIds)); - respVO.getCreateDeviceNames().add(importDevice.getDeviceName()); - return; - } - // 2.2.2 如果存在,判断是否允许更新 - if (!updateSupport) { - throw exception(DEVICE_KEY_EXISTS); - } - updateDevice(new IotDeviceSaveReqVO().setId(existDevice.getId()) - .setGatewayId(gatewayId).setGroupIds(groupIds)); - respVO.getUpdateDeviceNames().add(importDevice.getDeviceName()); - } catch (ServiceException ex) { - respVO.getFailureDeviceNames().put(importDevice.getDeviceName(), ex.getMessage()); - } - }); - return respVO; - } - - @Override - public IotDeviceAuthInfoRespVO getDeviceAuthInfo(Long id) { - IotDeviceDO device = validateDeviceExists(id); - // 使用 IotDeviceAuthUtils 生成认证信息 - IotDeviceAuthReqDTO authInfo = IotDeviceAuthUtils.getAuthInfo( - device.getProductKey(), device.getDeviceName(), device.getDeviceSecret()); - return BeanUtils.toBean(authInfo, IotDeviceAuthInfoRespVO.class); - } - - private void deleteDeviceCache(IotDeviceDO device) { - // 保证 Spring AOP 触发 - getSelf().deleteDeviceCache0(device); - } - - private void deleteDeviceCache(List devices) { - devices.forEach(this::deleteDeviceCache); - } - - @SuppressWarnings("unused") - @Caching(evict = { - @CacheEvict(value = RedisKeyConstants.DEVICE, key = "#device.id"), - @CacheEvict(value = RedisKeyConstants.DEVICE, key = "#device.productKey + '_' + #device.deviceName") - }) - public void deleteDeviceCache0(IotDeviceDO device) { - } - - @Override - public Long getDeviceCount(LocalDateTime createTime) { - return deviceMapper.selectCountByCreateTime(createTime); - } - - @Override - public Map getDeviceCountMapByProductId() { - return deviceMapper.selectDeviceCountMapByProductId(); - } - - @Override - public Map getDeviceCountMapByState() { - return deviceMapper.selectDeviceCountGroupByState(); - } - - @Override - public List getDeviceListByProductKeyAndNames(String productKey, List deviceNames) { - if (StrUtil.isBlank(productKey) || CollUtil.isEmpty(deviceNames)) { - return Collections.emptyList(); - } - return deviceMapper.selectByProductKeyAndDeviceNames(productKey, deviceNames); - } - - @Override - public boolean authDevice(IotDeviceAuthReqDTO authReqDTO) { - // 1. 校验设备是否存在 - IotDeviceIdentity deviceInfo = IotDeviceAuthUtils.parseUsername(authReqDTO.getUsername()); - if (deviceInfo == null) { - log.error("[authDevice][认证失败,username({}) 格式不正确]", authReqDTO.getUsername()); - return false; - } - String deviceName = deviceInfo.getDeviceName(); - String productKey = deviceInfo.getProductKey(); - IotDeviceDO device = getSelf().getDeviceFromCache(productKey, deviceName); - if (device == null) { - log.warn("[authDevice][设备({}/{}) 不存在]", productKey, deviceName); - return false; - } - - // 2. 校验密码 - IotDeviceAuthReqDTO authInfo = IotDeviceAuthUtils.getAuthInfo(productKey, deviceName, device.getDeviceSecret()); - if (ObjUtil.notEqual(authInfo.getPassword(), authReqDTO.getPassword())) { - log.error("[authDevice][设备({}/{}) 密码不正确]", productKey, deviceName); - return false; - } - return true; - } - - @Override - public List validateDeviceListExists(Collection ids) { - List devices = getDeviceList(ids); - if (devices.size() != ids.size()) { - throw exception(DEVICE_NOT_EXISTS); - } - return devices; - } - - @Override - public List getDeviceList(Collection ids) { - if (CollUtil.isEmpty(ids)) { - return Collections.emptyList(); - } - return deviceMapper.selectByIds(ids); - } - - @Override - public void updateDeviceFirmware(Long deviceId, Long firmwareId) { - // 1. 校验设备是否存在 - IotDeviceDO device = validateDeviceExists(deviceId); - - // 2. 更新设备固件版本 - IotDeviceDO updateObj = new IotDeviceDO().setId(deviceId).setFirmwareId(firmwareId); - deviceMapper.updateById(updateObj); - - // 3. 清空对应缓存 - deleteDeviceCache(device); - } - - @Override - public void updateDeviceLocation(IotDeviceDO device, BigDecimal longitude, BigDecimal latitude) { - // 1. 更新定位信息 - deviceMapper.updateById(new IotDeviceDO().setId(device.getId()) - .setLongitude(longitude).setLatitude(latitude)); - - // 2. 清空对应缓存 - deleteDeviceCache(device); - } - - @Override - public List getDeviceListByHasLocation() { - return deviceMapper.selectListByHasLocation(); - } - - // ========== 网关-拓扑管理(后台操作) ========== - - @Override - @Transactional(rollbackFor = Exception.class) - public void bindDeviceGateway(Collection subIds, Long gatewayId) { - if (CollUtil.isEmpty(subIds)) { - return; - } - // 1.1 校验网关设备存在且类型正确 - validateGatewayDeviceExists(gatewayId); - // 1.2 校验每个设备是否可绑定 - List devices = deviceMapper.selectByIds(subIds); - for (IotDeviceDO device : devices) { - checkSubDeviceCanBind(device, gatewayId); - } - - // 2. 批量更新数据库 - List updateList = convertList(devices, device -> - new IotDeviceDO().setId(device.getId()).setGatewayId(gatewayId)); - deviceMapper.updateBatch(updateList); - - // 3. 清空对应缓存 - deleteDeviceCache(devices); - - // 4. 下发网关设备拓扑变更通知(增加) - sendTopoChangeNotify(gatewayId, IotDeviceTopoChangeReqDTO.STATUS_CREATE, devices); - } - - private void checkSubDeviceCanBind(IotDeviceDO device, Long gatewayId) { - if (!IotProductDeviceTypeEnum.isGatewaySub(device.getDeviceType())) { - throw exception(DEVICE_NOT_GATEWAY_SUB, device.getProductKey(), device.getDeviceName()); - } - // 已绑定到其他网关,拒绝绑定(需先解绑) - if (device.getGatewayId() != null && ObjUtil.notEqual(device.getGatewayId(), gatewayId)) { - throw exception(DEVICE_GATEWAY_BINDTO_EXISTS, device.getProductKey(), device.getDeviceName()); - } - } - - @Override - @Transactional(rollbackFor = Exception.class) - public void unbindDeviceGateway(Collection subIds, Long gatewayId) { - // 1. 校验设备存在 - if (CollUtil.isEmpty(subIds)) { - return; - } - List devices = deviceMapper.selectByIds(subIds); - devices.removeIf(device -> ObjUtil.notEqual(device.getGatewayId(), gatewayId)); - if (CollUtil.isEmpty(devices)) { - return; - } - - // 2. 批量更新数据库(将 gatewayId 设置为 null) - deviceMapper.updateGatewayIdBatch(convertList(devices, IotDeviceDO::getId), null); - - // 3. 清空对应缓存 - deleteDeviceCache(devices); - - // 4. 下发网关设备拓扑变更通知(删除) - sendTopoChangeNotify(gatewayId, IotDeviceTopoChangeReqDTO.STATUS_DELETE, devices); - } - - @Override - public PageResult getUnboundSubDevicePage(IotDevicePageReqVO pageReqVO) { - return deviceMapper.selectUnboundSubDevicePage(pageReqVO); - } - - @Override - public List getDeviceListByGatewayId(Long gatewayId) { - return deviceMapper.selectListByGatewayId(gatewayId); - } - - // ========== 网关-拓扑管理(设备上报) ========== - - @Override - public List handleTopoAddMessage(IotDeviceMessage message, IotDeviceDO gatewayDevice) { - // 1.1 校验网关设备类型 - if (!IotProductDeviceTypeEnum.isGateway(gatewayDevice.getDeviceType())) { - throw exception(DEVICE_NOT_GATEWAY); - } - // 1.2 解析参数 - IotDeviceTopoAddReqDTO params = JsonUtils.convertObject(message.getParams(), IotDeviceTopoAddReqDTO.class); - if (params == null || CollUtil.isEmpty(params.getSubDevices())) { - throw exception(DEVICE_TOPO_PARAMS_INVALID); - } - - // 2. 遍历处理每个子设备 - List addedSubDevices = new ArrayList<>(); - for (IotDeviceAuthReqDTO subDeviceAuth : params.getSubDevices()) { - try { - IotDeviceDO subDevice = addDeviceTopo(gatewayDevice, subDeviceAuth); - addedSubDevices.add(new IotDeviceIdentity(subDevice.getProductKey(), subDevice.getDeviceName())); - } catch (Exception ex) { - log.warn("[handleTopoAddMessage][网关({}/{}) 添加子设备失败,message={}]", - gatewayDevice.getProductKey(), gatewayDevice.getDeviceName(), message, ex); - } - } - - // 3. 返回响应数据(包含成功添加的子设备列表) - return addedSubDevices; - } - - private IotDeviceDO addDeviceTopo(IotDeviceDO gatewayDevice, IotDeviceAuthReqDTO subDeviceAuth) { - // 1.1 解析子设备信息 - IotDeviceIdentity subDeviceInfo = IotDeviceAuthUtils.parseUsername(subDeviceAuth.getUsername()); - if (subDeviceInfo == null) { - throw exception(DEVICE_TOPO_SUB_DEVICE_USERNAME_INVALID); - } - // 1.2 校验子设备认证信息 - if (!authDevice(subDeviceAuth)) { - throw exception(DEVICE_TOPO_SUB_DEVICE_AUTH_FAILED); - } - // 1.3 获取子设备 - IotDeviceDO subDevice = getSelf().getDeviceFromCache(subDeviceInfo.getProductKey(), subDeviceInfo.getDeviceName()); - if (subDevice == null) { - throw exception(DEVICE_NOT_EXISTS); - } - // 1.4 校验子设备类型 - checkSubDeviceCanBind(subDevice, gatewayDevice.getId()); - - // 2. 更新数据库 - deviceMapper.updateById(new IotDeviceDO().setId(subDevice.getId()).setGatewayId(gatewayDevice.getId())); - log.info("[addDeviceTopo][网关({}/{}) 绑定子设备({}/{})]", - gatewayDevice.getProductKey(), gatewayDevice.getDeviceName(), - subDevice.getProductKey(), subDevice.getDeviceName()); - - // 3. 清空对应缓存 - deleteDeviceCache(subDevice); - return subDevice; - } - - @Override - public List handleTopoDeleteMessage(IotDeviceMessage message, IotDeviceDO gatewayDevice) { - // 1.1 校验网关设备类型 - if (!IotProductDeviceTypeEnum.isGateway(gatewayDevice.getDeviceType())) { - throw exception(DEVICE_NOT_GATEWAY); - } - // 1.2 解析参数 - IotDeviceTopoDeleteReqDTO params = JsonUtils.convertObject(message.getParams(), IotDeviceTopoDeleteReqDTO.class); - if (params == null || CollUtil.isEmpty(params.getSubDevices())) { - throw exception(DEVICE_TOPO_PARAMS_INVALID); - } - - // 2. 遍历处理每个子设备 - List deletedSubDevices = new ArrayList<>(); - for (IotDeviceIdentity subDeviceIdentity : params.getSubDevices()) { - try { - deleteDeviceTopo(gatewayDevice, subDeviceIdentity); - deletedSubDevices.add(subDeviceIdentity); - } catch (Exception ex) { - log.warn("[handleTopoDeleteMessage][网关({}/{}) 删除子设备失败,productKey={}, deviceName={}]", - gatewayDevice.getProductKey(), gatewayDevice.getDeviceName(), - subDeviceIdentity.getProductKey(), subDeviceIdentity.getDeviceName(), ex); - } - } - - // 3. 返回响应数据(包含成功删除的子设备列表) - return deletedSubDevices; - } - - private void deleteDeviceTopo(IotDeviceDO gatewayDevice, IotDeviceIdentity subDeviceIdentity) { - // 1.1 获取子设备 - IotDeviceDO subDevice = getSelf().getDeviceFromCache(subDeviceIdentity.getProductKey(), subDeviceIdentity.getDeviceName()); - if (subDevice == null) { - throw exception(DEVICE_NOT_EXISTS); - } - // 1.2 校验子设备是否绑定到该网关 - if (ObjUtil.notEqual(subDevice.getGatewayId(), gatewayDevice.getId())) { - throw exception(DEVICE_TOPO_SUB_NOT_BINDTO_GATEWAY, - subDeviceIdentity.getProductKey(), subDeviceIdentity.getDeviceName()); - } - - // 2. 更新数据库(将 gatewayId 设置为 null) - deviceMapper.updateGatewayIdBatch(singletonList(subDevice.getId()), null); - log.info("[deleteDeviceTopo][网关({}/{}) 解绑子设备({}/{})]", - gatewayDevice.getProductKey(), gatewayDevice.getDeviceName(), - subDevice.getProductKey(), subDevice.getDeviceName()); - - // 3. 清空对应缓存 - deleteDeviceCache(subDevice); - - // 4. 子设备下线 - if (Objects.equals(subDevice.getState(), IotDeviceStateEnum.ONLINE.getState())) { - updateDeviceState(subDevice, IotDeviceStateEnum.OFFLINE.getState()); - } - } - - @Override - public IotDeviceTopoGetRespDTO handleTopoGetMessage(IotDeviceDO gatewayDevice) { - // 1. 校验网关设备类型 - if (!IotProductDeviceTypeEnum.isGateway(gatewayDevice.getDeviceType())) { - throw exception(DEVICE_NOT_GATEWAY); - } - - // 2. 获取子设备列表并转换 - List subDevices = deviceMapper.selectListByGatewayId(gatewayDevice.getId()); - List subDeviceIdentities = convertList(subDevices, subDevice -> - new IotDeviceIdentity(subDevice.getProductKey(), subDevice.getDeviceName())); - return new IotDeviceTopoGetRespDTO().setSubDevices(subDeviceIdentities); - } - - /** - * 发送拓扑变更通知给网关设备 - * - * @param gatewayId 网关设备编号 - * @param status 变更状态(0-创建, 1-删除) - * @param subDevices 子设备列表 - * @see 阿里云 - 通知网关拓扑关系变化 - */ - private void sendTopoChangeNotify(Long gatewayId, Integer status, List subDevices) { - if (CollUtil.isEmpty(subDevices)) { - return; - } - // 1. 获取网关设备 - IotDeviceDO gatewayDevice = deviceMapper.selectById(gatewayId); - if (gatewayDevice == null) { - log.warn("[sendTopoChangeNotify][网关设备({}) 不存在,无法发送拓扑变更通知]", gatewayId); - return; - } - - try { - // 2.1 构建拓扑变更通知消息 - List subList = convertList(subDevices, subDevice -> - new IotDeviceIdentity(subDevice.getProductKey(), subDevice.getDeviceName())); - IotDeviceTopoChangeReqDTO params = new IotDeviceTopoChangeReqDTO(status, subList); - IotDeviceMessage notifyMessage = IotDeviceMessage.requestOf( - IotDeviceMessageMethodEnum.TOPO_CHANGE.getMethod(), params); - - // 2.2 发送消息 - deviceMessageService.sendDeviceMessage(notifyMessage, gatewayDevice); - log.info("[sendTopoChangeNotify][网关({}/{}) 发送拓扑变更通知成功,status={}, subDevices={}]", - gatewayDevice.getProductKey(), gatewayDevice.getDeviceName(), - status, subList); - } catch (Exception ex) { - log.error("[sendTopoChangeNotify][网关({}/{}) 发送拓扑变更通知失败,status={}]", - gatewayDevice.getProductKey(), gatewayDevice.getDeviceName(), status, ex); - } - } - - // ========== 设备动态注册 ========== - - @Override - public IotDeviceRegisterRespDTO registerDevice(IotDeviceRegisterReqDTO reqDTO) { - // 1.1 校验产品 - IotProductDO product = TenantUtils.executeIgnore(() -> - productService.getProductByProductKey(reqDTO.getProductKey())); - if (product == null) { - throw exception(PRODUCT_NOT_EXISTS); - } - // 1.2 校验产品是否开启动态注册 - if (BooleanUtil.isFalse(product.getRegisterEnabled())) { - throw exception(DEVICE_REGISTER_DISABLED); - } - // 1.3 【重要!!!】验证签名 - if (!IotProductAuthUtils.verifySign(reqDTO.getProductKey(), reqDTO.getDeviceName(), - product.getProductSecret(), reqDTO.getSign())) { - throw exception(DEVICE_REGISTER_SECRET_INVALID); - } - return TenantUtils.execute(product.getTenantId(), () -> { - // 1.4 校验设备是否已存在(已存在则不允许重复注册) - IotDeviceDO device = getSelf().getDeviceFromCache(reqDTO.getProductKey(), reqDTO.getDeviceName()); - if (device != null) { - throw exception(DEVICE_REGISTER_ALREADY_EXISTS); - } - - // 2.1 自动创建设备 - IotDeviceSaveReqVO createReqVO = new IotDeviceSaveReqVO() - .setDeviceName(reqDTO.getDeviceName()) - .setProductId(product.getId()); - device = createDevice0(createReqVO); - log.info("[registerDevice][产品({}) 自动创建设备({})]", - reqDTO.getProductKey(), reqDTO.getDeviceName()); - // 2.2 返回设备密钥 - return new IotDeviceRegisterRespDTO(device.getProductKey(), device.getDeviceName(), device.getDeviceSecret()); - }); - } - - @Override - public List registerSubDevices(IotSubDeviceRegisterFullReqDTO reqDTO) { - // 1. 校验网关设备 - IotDeviceDO gatewayDevice = getSelf().getDeviceFromCache(reqDTO.getGatewayProductKey(), reqDTO.getGatewayDeviceName()); - - // 2. 遍历注册每个子设备 - return TenantUtils.execute(gatewayDevice.getTenantId(), () -> - registerSubDevices0(gatewayDevice, reqDTO.getSubDevices())); - } - - @Override - public List handleSubDeviceRegisterMessage(IotDeviceMessage message, IotDeviceDO gatewayDevice) { - // 1. 解析参数 - if (!(message.getParams() instanceof List)) { - throw exception(DEVICE_SUB_REGISTER_PARAMS_INVALID); - } - List subDevices = JsonUtils.convertList(message.getParams(), IotSubDeviceRegisterReqDTO.class); - - // 2. 遍历注册每个子设备 - return registerSubDevices0(gatewayDevice, subDevices); - } - - private List registerSubDevices0(IotDeviceDO gatewayDevice, - List subDevices) { - // 1.1 校验网关设备 - if (gatewayDevice == null) { - throw exception(DEVICE_NOT_EXISTS); - } - if (!IotProductDeviceTypeEnum.isGateway(gatewayDevice.getDeviceType())) { - throw exception(DEVICE_NOT_GATEWAY); - } - // 1.2 注册设备不能为空 - if (CollUtil.isEmpty(subDevices)) { - throw exception(DEVICE_SUB_REGISTER_PARAMS_INVALID); - } - - // 2. 遍历注册每个子设备 - List results = new ArrayList<>(subDevices.size()); - for (IotSubDeviceRegisterReqDTO subDevice : subDevices) { - try { - IotDeviceDO device = registerSubDevice0(gatewayDevice, subDevice); - results.add(new IotSubDeviceRegisterRespDTO( - subDevice.getProductKey(), subDevice.getDeviceName(), device.getDeviceSecret())); - } catch (Exception ex) { - log.error("[registerSubDevices0][子设备({}/{}) 注册失败]", - subDevice.getProductKey(), subDevice.getDeviceName(), ex); - } - } - return results; - } - - private IotDeviceDO registerSubDevice0(IotDeviceDO gatewayDevice, IotSubDeviceRegisterReqDTO params) { - // 1.1 校验产品 - IotProductDO product = productService.getProductByProductKey(params.getProductKey()); - if (product == null) { - throw exception(PRODUCT_NOT_EXISTS); - } - // 1.2 校验产品是否为网关子设备类型 - if (!IotProductDeviceTypeEnum.isGatewaySub(product.getDeviceType())) { - throw exception(DEVICE_SUB_REGISTER_PRODUCT_NOT_GATEWAY_SUB, params.getProductKey()); - } - // 1.3 校验设备是否已存在(子设备动态注册:设备必须已预注册) - IotDeviceDO existDevice = getSelf().getDeviceFromCache(params.getProductKey(), params.getDeviceName()); - if (existDevice == null) { - throw exception(DEVICE_NOT_EXISTS); - } - // 1.4 校验是否绑定到其他网关 - if (existDevice.getGatewayId() != null && ObjUtil.notEqual(existDevice.getGatewayId(), gatewayDevice.getId())) { - throw exception(DEVICE_GATEWAY_BINDTO_EXISTS, - existDevice.getProductKey(), existDevice.getDeviceName()); - } - - // 2. 绑定到网关(如果尚未绑定) - if (existDevice.getGatewayId() == null) { - // 2.1 更新数据库 - deviceMapper.updateById(new IotDeviceDO().setId(existDevice.getId()).setGatewayId(gatewayDevice.getId())); - // 2.2 清空对应缓存 - deleteDeviceCache(existDevice); - log.info("[registerSubDevice][网关({}/{}) 绑定子设备({}/{})]", - gatewayDevice.getProductKey(), gatewayDevice.getDeviceName(), - existDevice.getProductKey(), existDevice.getDeviceName()); - } - return existDevice; - } - -} diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/device/TDengineService.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/device/TDengineService.java new file mode 100644 index 0000000..bfcf3c4 --- /dev/null +++ b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/device/TDengineService.java @@ -0,0 +1,2468 @@ +package cn.iocoder.yudao.module.iot.service.device; + +import cn.hutool.core.util.StrUtil; +import cn.iocoder.yudao.framework.common.pojo.DeviceEdgeData; +import cn.iocoder.yudao.framework.common.pojo.PageResult; +import cn.iocoder.yudao.module.iot.controller.admin.device.enums.JavaToTdengineTypeEnum; +import cn.iocoder.yudao.module.iot.controller.admin.deviceoperationrecord.vo.DeviceTotalTimeRecordRespVO; +import cn.iocoder.yudao.module.iot.controller.admin.devicewarinningrecord.vo.DeviceWarinningRecordPageReqVO; +import cn.iocoder.yudao.module.iot.dal.dataobject.devicecontactmodel.DeviceContactModelDO; +import cn.iocoder.yudao.module.iot.dal.dataobject.deviceoperationrecord.DeviceOperationRecordDO; +import cn.iocoder.yudao.module.iot.dal.dataobject.devicewarinningrecord.DeviceWarinningRecordDO; +import com.alibaba.fastjson.JSON; +import com.baomidou.dynamic.datasource.annotation.DS; +import com.baomidou.mybatisplus.core.toolkit.CollectionUtils; +import lombok.extern.slf4j.Slf4j; +import org.apache.commons.codec.DecoderException; +import org.apache.commons.codec.binary.Hex; +import org.apache.commons.lang3.StringUtils; +import org.springframework.dao.EmptyResultDataAccessException; +import org.springframework.jdbc.core.JdbcTemplate; +import org.springframework.jdbc.core.RowMapper; +import org.springframework.stereotype.Service; + +import javax.annotation.Resource; +import java.nio.charset.StandardCharsets; +import java.sql.ResultSet; +import java.sql.SQLException; +import java.sql.Timestamp; +import java.text.SimpleDateFormat; +import java.time.Instant; +import java.time.LocalDate; +import java.time.LocalDateTime; +import java.time.format.DateTimeFormatter; +import java.util.*; +import java.util.stream.Collectors; + +import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception; +import static cn.iocoder.yudao.module.iot.enums.ErrorCodeConstants.*; + +@Service +@Slf4j +public class TDengineService { + @Resource + private JdbcTemplate jdbcTemplate; + +// @DS("tdengine") +// public void testConnection() { +// String testSQL = "SELECT SERVER_STATUS()"; +// jdbcTemplate.queryForObject(testSQL, Integer.class); +// log.info("TDengine连接正常"); +// +// } + + @DS("tdengine") + public void initDatabaseAndTable(Long id) { + // 1. 创建数据库(使用正确的TDengine语法) + // 注意:KEEP 必须大于或等于 3 倍的 DURATION[6](@ref),建议调整 + String createDbSQL = "CREATE DATABASE IF NOT EXISTS besure KEEP 365 DURATION 30"; + jdbcTemplate.execute(createDbSQL); + + // 2. 使用数据库 + jdbcTemplate.execute("USE besure"); + + // 3. 创建超级表 + String createSuperTableSQL = "CREATE STABLE IF NOT EXISTS device_data (" + + "ts TIMESTAMP, " + + "query_data BLOB" + + ") TAGS (device_id BIGINT)"; + jdbcTemplate.execute(createSuperTableSQL); + + // 4. 创建子表 + String tableName = "d_" + id; + String createTableSql = String.format( + "CREATE TABLE IF NOT EXISTS %s USING device_data TAGS(%d)", + tableName, id); + jdbcTemplate.execute(createTableSql); + + log.info("TDengine表创建成功 {}", tableName); + + } + + + /** + * 根据设备ID查询对应子表的最新一条数据 + * 使用TDengine的last_row函数实现高效查询[3,4](@ref) + * + * @param id 设备ID,对应子表的tags值 + * @return 最新设备数据,包含时间戳和查询数据 + */ + @DS("tdengine") + public Map getLatestDeviceData(Long id) { + String tableName = "d_" + id; + + String sql = "SELECT ts, query_data FROM besure." + tableName + + " ORDER BY ts DESC LIMIT 1"; + + try { + return jdbcTemplate.queryForObject(sql, new RowMapper>() { + @Override + public Map mapRow(ResultSet rs, int rowNum) throws SQLException { + Map result = new HashMap<>(); + // 现在可以直接通过别名获取 + Timestamp ts = rs.getTimestamp("ts"); + // 将Timestamp格式化为字符串 + SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss"); + String timestampStr = sdf.format(ts); + result.put("timestamp", timestampStr); + + // 读取 query_data(二进制字段) + byte[] blob = rs.getBytes("query_data"); + result.put("queryData", decodeQueryData(blob)); + + + result.put("deviceId", id); + result.put("tableName", tableName); + return result; + } + }); + } catch (Exception e) { + log.error("查询设备id为 {} 的最新数据时发生异常", id, e); + return null; + } + } + + + + /** + * 向指定设备插入数据 + * @param id 设备ID + * @param queryData 查询数据(JSON字符串或其他文本数据) + * @return 插入是否成功 + */ + @DS("tdengine") + public boolean insertDeviceData(Long id, String queryData) { + return insertDeviceData(id, queryData, new Timestamp(System.currentTimeMillis())); + } + + /** + * 向指定设备插入带时间戳的数据 + * @param id 设备ID + * @param timestamp 时间戳 + * @return 插入是否成功 + */ + @DS("tdengine") + public boolean insertDeviceData(Long id, String jsonString, Timestamp timestamp) { + try { + // 确保使用正确的数据库 + jdbcTemplate.execute("USE besure"); + + String tableName = "d_" + id; + String sql = "INSERT INTO besure." + tableName + " (ts, query_data) VALUES (?, ?)"; + + return jdbcTemplate.update(sql, ps -> { + ps.setTimestamp(1, timestamp); + // JSON字符串转byte数组 + byte[] blobData = jsonString.getBytes(StandardCharsets.UTF_8); + ps.setBytes(2, blobData); + }) > 0; + } catch (Exception e) { + log.error("向设备id为 {} 插入数据时发生异常", id, e); + return false; + } + } + + /** + * 查询指定设备表的全部数据并按时间倒序排序 + * @param id 设备ID + * @return 设备数据列表,按时间戳倒序排列 + */ + @DS("tdengine") + public List> getNewestDeviceDataOrderByTimeDesc(Long id) { + String tableName = "d_" + id; + String sql = "SELECT ts, query_data FROM besure." + tableName + " ORDER BY ts DESC LIMIT 1"; + + try { + return Collections.singletonList(jdbcTemplate.queryForObject(sql, new RowMapper>() { + @Override + public Map mapRow(ResultSet rs, int rowNum) throws SQLException { + Map result = new HashMap<>(); + result.put("timestamp", rs.getTimestamp("ts")); + result.put("deviceId", id); + + // 读取 query_data(二进制字段) + byte[] blob = rs.getBytes("query_data"); + result.put("queryData", decodeQueryData(blob)); + + return result; + } + })); + } catch (EmptyResultDataAccessException e) { + return Collections.singletonList(createEmptyResult(id)); + } catch (Exception e) { + log.error("查询设备id为 {} 的最新数据时发生异常", id, e); + return Collections.singletonList(createEmptyResult(id)); + } + } + + + // 检查是否是十六进制字符串 + private boolean isHexString(String str) { + if (str == null || str.trim().isEmpty()) { + return false; + } + String s = str.trim(); + // 检查是否只包含十六进制字符(0-9, a-f, A-F) + return s.matches("^[0-9a-fA-F]+$"); + } + + // 十六进制解码 + private String hexToString(String hex) { + try { + // 使用 Apache Commons Codec + byte[] bytes = Hex.decodeHex(hex); + return new String(bytes, StandardCharsets.UTF_8); + } catch (DecoderException e) { + log.error("十六进制解码失败: {}", e); + return ""; + } + } + + + private Map createEmptyResult(Long deviceId) { + Map result = new HashMap<>(); + result.put("deviceId", deviceId); + result.put("queryData", new ArrayList<>()); + result.put("timestamp", null); + return result; + } + /** + * 查询指定设备表的全部数据并按时间倒序排序 + * @param id 设备ID + * @return 设备数据列表,按时间戳倒序排列 + */ + @DS("tdengine") + public List> getstDeviceDataOrderByTimeDesc(Long id,String StartTime, String EndTime,Integer limit) { + String tableName = "d_" + id; + StringBuilder sqlBuilder = new StringBuilder(); + List params = new ArrayList<>(); + + sqlBuilder.append("SELECT ts, query_data FROM besure.").append(tableName).append(" WHERE 1=1"); + + if (StartTime != null) { + // 直接将时间字符串拼接到SQL中 + sqlBuilder.append(" AND ts >= '").append(StartTime).append("'"); + } + + if (EndTime != null) { + sqlBuilder.append(" AND ts <= '").append(EndTime).append("'"); + } + + sqlBuilder.append(" ORDER BY ts DESC"); + + // 添加LIMIT限制 + if (limit != null && limit > 0) { + sqlBuilder.append(" LIMIT ").append(limit); + } + + try { + return jdbcTemplate.query(sqlBuilder.toString(), new RowMapper>() { + @Override + public Map mapRow(ResultSet rs, int rowNum) throws SQLException { + Map result = new HashMap<>(); + result.put("timestamp", rs.getTimestamp("ts")); + result.put("deviceId", id); + + // 读取 query_data(二进制字段) + byte[] blob = rs.getBytes("query_data"); + result.put("queryData", decodeQueryData(blob)); + + return result; + } + }); + } catch (Exception e) { +// log.error("查询设备" + id + "的最新数据时发生异常", e); +// e.printStackTrace(); + return Collections.singletonList(createEmptyResult(id)); + } + } + + + /** + * 查询指定设备表的全部数据并按时间倒序排序 + * @param id 设备ID + * @return 设备数据列表,按时间戳倒序排列 + */ + @DS("tdengine") + public List> getstDeviceDataOrderByTimeDescPage(Long id,String StartTime, String EndTime,Integer page, + Integer pageSize) { + String tableName = "d_" + id; + StringBuilder sqlBuilder = new StringBuilder(); + List params = new ArrayList<>(); + + sqlBuilder.append("SELECT ts, query_data FROM besure.").append(tableName).append(" WHERE 1=1"); + + if (StartTime != null) { + // 直接将时间字符串拼接到SQL中 + sqlBuilder.append(" AND ts >= '").append(StartTime).append("'"); + } + + if (EndTime != null) { + sqlBuilder.append(" AND ts <= '").append(EndTime).append("'"); + } + + sqlBuilder.append(" ORDER BY ts DESC"); + + // 分页处理 + if (page != null && pageSize != null && page > 0 && pageSize > 0) { + int offset = (page - 1) * pageSize; + sqlBuilder.append(" LIMIT ").append(offset).append(",").append(pageSize); + } + + try { + return jdbcTemplate.query(sqlBuilder.toString(), new RowMapper>() { + @Override + public Map mapRow(ResultSet rs, int rowNum) throws SQLException { + Map result = new HashMap<>(); + result.put("timestamp", rs.getTimestamp("ts")); + result.put("deviceId", id); + + // 读取 query_data(二进制字段) + byte[] blob = rs.getBytes("query_data"); + result.put("queryData", decodeQueryData(blob)); + + return result; + } + }); + } catch (Exception e) { +// log.error("查询设备" + id + "的最新数据时发生异常", e); +// e.printStackTrace(); + return Collections.singletonList(createEmptyResult(id)); + } + } + + + /** + * 查询指定设备历史数据的总条数 + * + * @param deviceId 设备ID + * @param startTime 开始时间,可为空 + * @param endTime 结束时间,可为空 + * @return 总记录数 + */ + @DS("tdengine") + public long queryDeviceDataTotal(Long deviceId, String startTime, String endTime) { + if (deviceId == null) { + return 0L; + } + + String tableName = "d_" + deviceId; + StringBuilder countSql = new StringBuilder("SELECT COUNT(*) FROM besure." + tableName + " WHERE 1=1"); + + if (startTime != null) { + countSql.append(" AND ts >= '").append(startTime).append("'"); + } + + if (endTime != null) { + countSql.append(" AND ts <= '").append(endTime).append("'"); + } + + try { + Long total = jdbcTemplate.queryForObject(countSql.toString(), Long.class); + return total != null ? total : 0L; + } catch (Exception e) { + log.error("查询设备 {} 总条数异常", deviceId, e); + return 0L; + } + } + + + /** + * 从 TDengine 中查询设备边缘数据 + * + * 说明: + * 1. TDengine 中每个设备对应一张子表,表名规则:d_{deviceId} + * 2. 根据时间范围查询 + * 3. 根据 latest 参数决定取最新一条还是最早一条 + * + * @param deviceId 设备ID,同时也是子表后缀 + * @param startTime 起始时间(可以为 null) + * @param endTime 结束时间(可以为 null) + * @param latest true 表示取最新一条,false 表示取最早一条 + * @return 查询结果,包含 timestamp / deviceId / queryData + */ + @DS("tdengine") + public Map getDeviceEdgeData( + Long deviceId, + String startTime, + String endTime, + boolean latest) { + + // TDengine 子表名:d_设备ID + String tableName = "d_" + deviceId; + + // 构建 SQL + StringBuilder sql = new StringBuilder(); + sql.append("SELECT ts, query_data ") + .append("FROM besure.").append(tableName) + .append(" WHERE 1=1 "); + + // 起始时间条件 + if (startTime != null) { + sql.append(" AND ts >= '").append(startTime).append("' "); + } + + // 结束时间条件 + if (endTime != null) { + sql.append(" AND ts <= '").append(endTime).append("' "); + } + + // 根据 latest 决定排序方式 + // latest = true -> 按时间倒序,取最新一条 + // latest = false -> 按时间正序,取最早一条 + sql.append(" ORDER BY ts ") + .append(latest ? "DESC" : "ASC") + .append(" LIMIT 1"); + + try { + return jdbcTemplate.query(sql.toString(), rs -> { + + // 没有数据直接返回 null + if (!rs.next()) { + return null; + } + + Map result = new HashMap<>(); + result.put("timestamp", rs.getTimestamp("ts")); + result.put("deviceId", deviceId); + + // 读取 query_data(二进制字段) + byte[] blob = rs.getBytes("query_data"); + result.put("queryData", decodeQueryData(blob)); + + return result; + }); + } catch (Exception e) { + log.error("TDengine 查询失败,deviceId={} ,td表不存在", deviceId,e); + return null; + } + } + + + /** + * 批量获取设备在时间区间的首末数据 + * @param deviceIds 设备ID列表 + * @param startTime 开始时间, 格式 yyyy-MM-dd HH:mm:ss + * @param endTime 结束时间, 格式 yyyy-MM-dd HH:mm:ss + * @return Map + */ + @DS("tdengine") + public Map queryDeviceFirstAndLast(Set deviceIds, String startTime, String endTime) { + if (deviceIds == null || deviceIds.isEmpty()) { + return Collections.emptyMap(); + } + + Map result = new HashMap<>(); + + for (Long deviceId : deviceIds) { + try { + // 查询首条数据 + List> firstList = getDeviceFirstData(deviceId, startTime, endTime, 1); + Map firstData = firstList.isEmpty() ? null : firstList.get(0); + + // 查询末条数据 + List> lastList = getDeviceLastData(deviceId, startTime, endTime, 1); + Map lastData = lastList.isEmpty() ? null : lastList.get(0); + + DeviceEdgeData edgeData = new DeviceEdgeData(); + edgeData.setDeviceId(deviceId); + + if (firstData != null) { + edgeData.setFirstTs(((Timestamp) firstData.get("timestamp")).toLocalDateTime()); + edgeData.setFirstData(JSON.toJSONString(firstData.get("queryData"))); + } + + if (lastData != null) { + edgeData.setLastTs(((Timestamp) lastData.get("timestamp")).toLocalDateTime()); + edgeData.setLastData(JSON.toJSONString(lastData.get("queryData"))); + } + + result.put(deviceId, edgeData); + } catch (Exception e) { + log.error("查询设备首末数据失败, deviceId={}", deviceId, e); + } + } + + return result; + } + + + + @DS("tdengine") + public List> getDeviceFirstData( + Long deviceId, + String startTime, + String endTime, + Integer limit) { + + String tableName = "d_" + deviceId; + StringBuilder sql = new StringBuilder(); + + sql.append("SELECT ts, query_data ") + .append("FROM besure.").append(tableName) + .append(" WHERE ts >= '").append(startTime).append("'") + .append(" AND ts <= '").append(endTime).append("'") + .append(" ORDER BY ts ASC "); + + if (limit != null && limit > 0) { + sql.append(" LIMIT ").append(limit); + } + + return jdbcTemplate.query(sql.toString(), (rs, rowNum) -> { + Map map = new HashMap<>(); + map.put("timestamp", rs.getTimestamp("ts")); + map.put("deviceId", deviceId); + + // 读取 query_data(二进制字段) + byte[] blob = rs.getBytes("query_data"); + map.put("queryData", decodeQueryData(blob)); + + return map; + }); + } + + @DS("tdengine") + public List> getDeviceLastData( + Long deviceId, + String startTime, + String endTime, + Integer limit) { + + String tableName = "d_" + deviceId; + StringBuilder sql = new StringBuilder(); + + sql.append("SELECT ts, query_data ") + .append("FROM besure.").append(tableName) + .append(" WHERE ts >= '").append(startTime).append("'") + .append(" AND ts <= '").append(endTime).append("'") + .append(" ORDER BY ts DESC "); + + if (limit != null && limit > 0) { + sql.append(" LIMIT ").append(limit); + } + + return jdbcTemplate.query(sql.toString(), (rs, rowNum) -> { + Map map = new HashMap<>(); + map.put("timestamp", rs.getTimestamp("ts")); + map.put("deviceId", deviceId); + + // 读取 query_data(二进制字段) + byte[] blob = rs.getBytes("query_data"); + map.put("queryData", decodeQueryData(blob)); + + + return map; + }); + } + + + + @DS("tdengine") + public List> queryLastDataByHour(Long deviceId, LocalDateTime startTime, LocalDateTime endTime) { + if (deviceId == null) { + return Collections.emptyList(); + } + + String tableName = "d_" + deviceId; + StringBuilder sql = new StringBuilder(); + sql.append("SELECT ts, query_data FROM besure.").append(tableName) + .append(" WHERE ts >= '").append(startTime.format(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss"))).append("'") + .append(" AND ts <= '").append(endTime.format(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss"))).append("'") + .append(" ORDER BY ts ASC"); // 升序,方便取每小时最后一条 + + return jdbcTemplate.query(sql.toString(), (rs, rowNum) -> { + Map map = new HashMap<>(); + map.put("deviceId", deviceId); + + Timestamp ts = rs.getTimestamp("ts"); + map.put("timestamp", ts != null ? ts.toLocalDateTime() : null); + + // 读取 query_data(二进制字段) + byte[] blob = rs.getBytes("query_data"); + map.put("queryData", decodeQueryData(blob)); + + + return map; + }); + } + + + + @DS("tdengine") + public List> queryLastDataByHourBatch( + Set deviceIds, + LocalDateTime startTime, + LocalDateTime endTime) { + + List> result = new ArrayList<>(); + DateTimeFormatter fmt = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss"); + + for (Long deviceId : deviceIds) { + String sql = + "SELECT ts, query_data FROM besure.d_" + deviceId + + " WHERE ts >= '" + startTime.format(fmt) + "'" + + " AND ts <= '" + endTime.format(fmt) + "'" + + " ORDER BY ts ASC"; + + jdbcTemplate.query(sql, rs -> { + Map map = new HashMap<>(); + map.put("deviceId", deviceId); + map.put("timestamp", rs.getTimestamp("ts").toLocalDateTime()); + map.put("queryData", decodeQueryData(rs.getBytes("query_data"))); + result.add(map); + }); + } + return result; + } + + + @DS("tdengine") + public List> queryLastDataByDaySafe( + Set deviceIds, + LocalDate startDate, + int days + ) { + List> result = new ArrayList<>(); + + DateTimeFormatter formatter = + DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss"); + + for (int i = 0; i < days; i++) { + + LocalDate day = startDate.plusDays(i); + LocalDateTime dayStart = day.atStartOfDay(); + LocalDateTime dayEnd = day.plusDays(1).atStartOfDay(); + + for (Long deviceId : deviceIds) { + + String tableName = "besure.d_" + deviceId; + + String sql = + "SELECT ts, query_data FROM " + tableName + + " WHERE ts >= '" + dayStart.format(formatter) + "'" + + " AND ts < '" + dayEnd.format(formatter) + "'" + + " ORDER BY ts DESC LIMIT 1"; + + try { + List> rows = jdbcTemplate.query( + sql, + (rs, rowNum) -> { + Map map = new HashMap<>(); + map.put("deviceId", deviceId); + map.put("day", day.toString()); + + Timestamp ts = rs.getTimestamp("ts"); + map.put("timestamp", + ts != null ? ts.toLocalDateTime() : null); + + byte[] blob = rs.getBytes("query_data"); + map.put("queryData", decodeQueryData(blob)); + + return map; + } + ); + + if (!rows.isEmpty()) { + result.add(rows.get(0)); + } + + } catch (Exception e) { + // 表不存在 / 查询失败 → 直接跳过 + log.warn("Skip TDengine table: {}, reason: {}", + tableName, e.getMessage()); + } + } + } + + return result; + } + + + + private String decodeQueryData(byte[] blob) { + if (blob == null || blob.length == 0) return "[]"; + + String json = new String(blob, StandardCharsets.UTF_8).trim(); + + // 去掉外层引号 + if (json.startsWith("\"") && json.endsWith("\"")) { + json = json.substring(1, json.length() - 1); + } + + // 如果是十六进制字符串,解码 + if (isHexString(json)) { + json = hexToString(json); + } + + return json; + } + + + @DS("tdengine") + public LocalDateTime selectLatestTs(Long deviceId) { + String sql = "SELECT ts FROM besure.d_" + deviceId + " ORDER BY ts DESC LIMIT 1"; + + final LocalDateTime[] latestTs = {null}; + + jdbcTemplate.query(sql, rs -> { + if (rs.next()) { + latestTs[0] = rs.getTimestamp("ts").toLocalDateTime(); + } + }); + + return latestTs[0]; + } + + + @DS("tdengine") + public Map selectLatestTsBatch(List deviceIds) { + Map result = new HashMap<>(); + + for (Long deviceId : deviceIds) { + String tableName = "besure.d_" + deviceId; + String sql = "SELECT ts FROM " + tableName + " ORDER BY ts DESC LIMIT 1"; + + try { + LocalDateTime ts = jdbcTemplate.queryForObject(sql, (rs, rowNum) -> rs.getTimestamp("ts").toLocalDateTime()); + result.put(deviceId, ts); + } catch (Exception e) { + // 表不存在或者查询失败 + result.put(deviceId, null); + log.warn("设备: {}, 获取 ts 失败: {}", deviceId, e.getMessage()); + } + } + + return result; + } + + + //=================================修改数据后新增方法=============================== + + + @DS("tdengine") + public void createTdengineTable(Long deviceId, List contactModelList) { + if (deviceId == null) { + return; + } + + // 1. 数据库名 + String dbName = "besure_server"; + String tableName = "d_" + deviceId; + + // 2. 确保数据库存在 + try { + String createDbSql = "CREATE DATABASE IF NOT EXISTS " + dbName; + jdbcTemplate.execute(createDbSql); + log.info("TDengine 数据库确保存在: {}", dbName); + } catch (Exception e) { + log.error("TDengine 数据库创建失败: {}", dbName, e); + throw exception(TABLE_CREATION_FAILED); + } + + // 3. 构建列SQL + StringBuilder columnsSql = new StringBuilder("ts TIMESTAMP"); + + // 4. 生成唯一列名 + String uniqueColName = "val_" + deviceId + "_" + System.currentTimeMillis(); + + // 5. 如果 contactModelList 不为空,添加对应列 + if (contactModelList != null && !contactModelList.isEmpty()) { + for (DeviceContactModelDO contact : contactModelList) { + String attributeCode = contact.getAttributeCode(); + String dataType = contact.getDataType(); + + if (StrUtil.isBlank(attributeCode) || StrUtil.isBlank(dataType)) { + continue; + } + + String tdType = JavaToTdengineTypeEnum.getTdTypeByJavaType(dataType); + if (tdType == null) { + tdType = "DOUBLE"; + } + + columnsSql.append(", ").append(attributeCode).append(" ").append(tdType); + } + + // 确保至少有一个数据列 + if (columnsSql.toString().equals("ts TIMESTAMP")) { + columnsSql.append(", ").append(uniqueColName).append(" DOUBLE"); // 使用唯一列名 + } + } else { + // 6. contactModelList 为空时,添加默认数据列 + columnsSql.append(", ").append(uniqueColName).append(" DOUBLE"); // 使用唯一列名 + } + + // 7. 构建完整 SQL + String fullTableName = dbName + "." + tableName; + String createTableSql = "CREATE TABLE IF NOT EXISTS " + fullTableName + + " (" + columnsSql.toString() + ")"; + + // 8. 执行创建表 + try { + jdbcTemplate.execute(createTableSql); + log.info("TDengine 表创建成功: {}", fullTableName); + } catch (Exception e) { + log.error("TDengine 表创建失败: {}", fullTableName, e); + throw exception(TABLE_CREATION_FAILED); + } + } + + + /** + * 新增td数据列 + * @param deviceId + * @param deviceContactModel + */ + @DS("tdengine") + public void AddTDDatabaseColumn(Long deviceId, DeviceContactModelDO deviceContactModel) { + + if (deviceId == null || deviceContactModel == null) { + return; + } + + String attributeCode = deviceContactModel.getAttributeCode(); + String dataType = deviceContactModel.getDataType(); + + if (attributeCode == null || dataType == null) { + return; + } + + // 表名 + String tableName = "besure_server.d_" + deviceId; + + // Java类型 -> TDengine类型 + String tdType = JavaToTdengineTypeEnum.getTdTypeByJavaType(dataType); + if (tdType == null) { + tdType = "DOUBLE"; + } + + // ALTER TABLE SQL + String alterSql = "ALTER TABLE " + tableName + + " ADD COLUMN " + attributeCode + " " + tdType; + + try { + jdbcTemplate.execute(alterSql); + log.info("TDengine 表新增列成功: table={}, column={}, type={}", + tableName, attributeCode, tdType); + + } catch (Exception e) { + + // TDengine 如果列已存在会报错,这里可以忽略 +// if (e.getMessage() != null && e.getMessage().contains("duplicated column name")) { +// log.warn("列已存在,跳过: table={}, column={}", tableName, attributeCode); +// return; +// } + + log.error("TDengine 表新增列失败: table={}, column={}", tableName, attributeCode, e); + throw exception(COLOUMN_CREATION_FAILED); + } + } + + + + /** + * 插入数据 + * @param deviceId + * @param dataList + * @return + */ + @DS("tdengine") + public boolean newInsertDeviceData(Long deviceId, List dataList) { + + if (deviceId == null || dataList == null || dataList.isEmpty()) { + return false; + } + + // 表名 + String tableName = "besure_server.d_" + deviceId; + + // 列名构建 + StringBuilder columnBuilder = new StringBuilder("ts"); + + // 值占位符 + StringBuilder valueBuilder = new StringBuilder("?"); + + // 参数列表 + List params = new ArrayList<>(); + + // TDengine 必须字段 + params.add(new Timestamp(System.currentTimeMillis())); + + for (DeviceContactModelDO model : dataList) { + + if (model == null) { + continue; + } + + String column = model.getAttributeCode(); + Object valueObj = model.getAddressValue(); + String javaType = model.getDataType(); + + // 1. 基础校验 + if (column == null || column.trim().isEmpty()) { + continue; + } + + if (javaType == null || javaType.trim().isEmpty()) { + javaType = "double"; + } + + // 防止 SQL 注入(只允许字母数字下划线) + if (!column.matches("^[a-zA-Z0-9_]+$")) { + log.warn("非法列名,跳过: {}", column); + continue; + } + + try { + + // 2. 自动新增列 +// try { +// AddTDDatabaseColumn(deviceId, model); +// } catch (Exception ignored) { +// // 忽略已存在异常 +// } + + // 3. 值为空 -> 插入 NULL + Object convertedValue = null; + + if (valueObj != null) { + + String value = valueObj.toString(); + + if (!value.isEmpty()) { + + convertedValue = + JavaToTdengineTypeEnum.convertValue(javaType, value); + + } + } + + // 4. 拼接 SQL + columnBuilder.append(", ").append(column); + + valueBuilder.append(", ?"); + + params.add(convertedValue); + + } catch (Exception e) { + + log.error("TDengine 数据处理失败: column={}, value={}, type={}", + column, valueObj, javaType, e); + } + } + + // 没有有效字段不插入 + if (params.size() <= 1) { + + log.warn("TDengine 插入数据为空,跳过 deviceId={}", deviceId); + + return true; + } + + String sql = + "INSERT INTO " + + tableName + + " (" + columnBuilder + ") VALUES (" + valueBuilder + ")"; + + try { + + jdbcTemplate.update(sql, params.toArray()); + + log.info("TDengine 插入成功: table={}, columnCount={}", + tableName, params.size() - 1); + + + } catch (Exception e) { + + log.error("TDengine 插入失败: table={}, sql={}", tableName, sql, e); + return false; + } + + return true; + } + + + /** + * 修改TDengine表列名 + * @param deviceId 设备ID + * @param oldColumnName 原列名 + * @param newColumnName 新列名 + */ + @DS("tdengine") + public void renameTDColumn(Long deviceId, String oldColumnName, String newColumnName) { + if (deviceId == null || StrUtil.isBlank(oldColumnName) || StrUtil.isBlank(newColumnName)) { + log.warn("修改列名参数错误: deviceId={}, oldColumnName={}, newColumnName={}", + deviceId, oldColumnName, newColumnName); + return; + } + + // 1. 验证原列是否存在 + if (!columnExists(deviceId, oldColumnName)) { + log.warn("原列不存在,无法修改: deviceId={}, column={}", deviceId, oldColumnName); + throw exception(COLUMN_RENAME_FAILED, "原列 '" + oldColumnName + "' 不存在"); + } + + // 2. 验证新列名是否已存在 + if (columnExists(deviceId, newColumnName)) { + log.warn("新列名已存在: deviceId={}, column={}", deviceId, newColumnName); + throw exception(COLUMN_RENAME_FAILED, "新列名 '" + newColumnName + "' 已存在"); + } + + // 3. 验证新列名是否为保留关键字 + if (isReservedKeyword(newColumnName)) { + log.warn("新列名是保留关键字: {}", newColumnName); + throw exception(COLUMN_RENAME_FAILED, "新列名 '" + newColumnName + "' 是保留关键字"); + } + + + // 表名 + String tableName = "besure_server.d_" + deviceId; + + // ALTER TABLE RENAME COLUMN SQL + String alterSql = "ALTER TABLE " + tableName + + " RENAME COLUMN " + oldColumnName + " " + newColumnName; + + try { + jdbcTemplate.execute(alterSql); + log.info("TDengine 表修改列名成功: table={}, oldColumn={}, newColumn={}", + tableName, oldColumnName, newColumnName); + + } catch (Exception e) { + // 处理特定错误 + String errorMsg = e.getMessage(); + + if (errorMsg != null) { + if (errorMsg.contains("column not exist") || errorMsg.contains("column does not exist")) { + log.warn("原列不存在,无法修改: table={}, column={}", tableName, oldColumnName); + } + + if (errorMsg.contains("duplicate column") || errorMsg.contains("column already exists")) { + log.warn("新列名已存在: table={}, newColumn={}", tableName, newColumnName); + } + + if (errorMsg.contains("reserved keyword") || errorMsg.toLowerCase().contains("syntax")) { + log.warn("新列名包含保留关键字: {}", newColumnName); + } + } + + log.error("TDengine 表修改列名失败: table={}, oldColumn={}, newColumn={}", + tableName, oldColumnName, newColumnName, e); + throw exception(COLUMN_RENAME_FAILED); + } + } + + /** + * 计算序号(线程安全版本)- 带详细日志 + */ + @DS("tdengine") + public synchronized int calculateSequence(Long deviceId, String originalName, String date) { + log.info("=== 开始计算序号 ==="); + log.info("参数: deviceId={}, originalName={}, date={}", deviceId, originalName, date); + + String tableName = "besure_server.d_" + deviceId; + log.info("表名: {}", tableName); + + try { + // 检查表是否存在 + boolean exists = tableExists(deviceId); + log.info("表是否存在: {}", exists); + + if (!exists) { + log.info("表不存在,返回 1"); + return 1; + } + + // 查询所有列 + String sql = "DESC " + tableName; + log.info("执行SQL: {}", sql); + + List> columns = jdbcTemplate.queryForList(sql); + log.info("查询到 {} 列", columns.size()); + + // 打印所有列 + for (int i = 0; i < columns.size(); i++) { + Map column = columns.get(i); + String colName = (String) column.get("Field"); + String colType = (String) column.get("Type"); + log.info("列[{}]: {} ({})", i, colName, colType); + } + + int maxSequence = 0; + String prefix = "del_" + originalName + "_" + date + "_"; + log.info("查找前缀: {}", prefix); + log.info("前缀长度: {}", prefix.length()); + + for (Map column : columns) { + String colName = (String) column.get("Field"); + log.info("检查列: {}", colName); + + if (colName != null) { + log.info("列长度: {}, 是否以前缀开头: {}", + colName.length(), colName.startsWith(prefix)); + + if (colName.startsWith(prefix)) { + String seqStr = colName.substring(prefix.length()); + log.info("匹配成功! 提取序号字符串: '{}'", seqStr); + + try { + int seq = Integer.parseInt(seqStr); + log.info("转换为数字: {}", seq); + if (seq > maxSequence) { + maxSequence = seq; + log.info("更新最大序号为: {}", maxSequence); + } + } catch (NumberFormatException e) { + log.warn("序号不是数字: '{}'", seqStr); + } + } + } + } + + int result = maxSequence + 1; + log.info("最终结果: {} + 1 = {}", maxSequence, result); + log.info("=== 计算序号结束 ==="); + return result; + + } catch (Exception e) { + log.error("计算历史列序号失败,返回1", e); + return 1; + } + } + + /** + * 检查表是否存在(修复版) + */ + @DS("tdengine") + private boolean tableExists(Long deviceId) { + String tableName = "besure_server.d_" + deviceId; + + try { + // 方法1:直接尝试查询 + String sql = "SELECT 1 FROM " + tableName + " LIMIT 0"; + jdbcTemplate.execute(sql); + return true; + + } catch (Exception e) { + // 如果错误包含"table not exist",说明表不存在 + String errorMsg = e.getMessage(); + if (errorMsg != null && ( + errorMsg.contains("table not exist") || + errorMsg.contains("table does not exist") || + errorMsg.contains("unknown table") || + errorMsg.contains("Table not found"))) { + return false; + } + + // 其他错误,可能是权限问题等 + log.warn("检查表是否存在时出错: table={}, error={}", tableName, errorMsg); + return false; + } + } + + + /** + * 检查列是否存在(增强版) + */ + @DS("tdengine") + private boolean columnExists(Long deviceId, String columnName) { + if (deviceId == null || StrUtil.isBlank(columnName)) { + return false; + } + + String tableName = "besure_server.d_" + deviceId; + + try { + // 方法1:直接尝试查询该列 + String testSql = "SELECT " + columnName + " FROM " + tableName + " LIMIT 0"; + jdbcTemplate.execute(testSql); + return true; // 执行成功,说明列存在 + + } catch (Exception e) { + String errorMsg = e.getMessage(); + + // 判断是否是"列不存在"的错误 + if (errorMsg != null && ( + errorMsg.contains("column not exist") || + errorMsg.contains("column does not exist") || + errorMsg.contains("Invalid column") || + errorMsg.contains("column not found") || + errorMsg.contains("unknown column"))) { + return false; // 列不存在 + } + + // 其他错误(如表不存在),记录日志 + log.warn("检查列是否存在时发生未知错误: table={}, column={}, error={}", + tableName, columnName, errorMsg); + return false; + } + } + + + + /** + * 检查是否为保留关键字 + */ + private boolean isReservedKeyword(String columnName) { + Set reservedWords = new HashSet<>(Arrays.asList( + "value", "timestamp", "current", "database", "table", + "user", "password", "select", "insert", "update", "delete", + "create", "drop", "alter", "show", "describe", "use", "ts" + )); + + return reservedWords.contains(columnName.toLowerCase()); + } + + + /** + * 验证列名是否符合TDengine规则 + */ + public void validateColumnName(String columnName) { + if (StrUtil.isBlank(columnName)) { + throw exception(DEVICE_MODEL_POINT_CODE_EXISTS, "列名不能为空"); + } + + // TDengine 列名规则验证 + // 1. 不能是保留关键字 + Set reservedKeywords = new HashSet<>(Arrays.asList( + "value", "timestamp", "current", "database", "table", "user", "password", + "select", "insert", "update", "delete", "create", "drop", "alter", + "show", "describe", "use", "ts", "now", "current_timestamp" + )); + + if (reservedKeywords.contains(columnName.toLowerCase())) { + throw exception(DEVICE_MODEL_POINT_CODE_EXISTS, + "列名不能使用保留关键字: " + columnName); + } + + // 2. 必须以字母开头 + if (!Character.isLetter(columnName.charAt(0))) { + throw exception(DEVICE_MODEL_POINT_CODE_EXISTS, + "列名必须以字母开头: " + columnName); + } + + // 3. 只能包含字母、数字、下划线 + if (!columnName.matches("^[a-zA-Z_][a-zA-Z0-9_]*$")) { + throw exception(DEVICE_MODEL_POINT_CODE_EXISTS, + "列名只能包含字母、数字和下划线: " + columnName); + } + + // 4. 长度限制(根据TDengine文档) + if (columnName.length() > 64) { + throw exception(DEVICE_MODEL_POINT_CODE_EXISTS, + "列名长度不能超过64个字符: " + columnName); + } + + // 5. 不能以下划线开头(虽然不是强制,但避免潜在问题) + if (columnName.startsWith("_")) { + throw exception(DEVICE_MODEL_POINT_CODE_EXISTS, + "列名不能以下划线开头: " + columnName); + } + } + + + /** + * 根据deviceId批量查询最新时间 + * @param deviceIds + * @return + */ + @DS("tdengine") + public Map newSelectLatestTsBatch(List deviceIds) { + Map result = new HashMap<>(); + + for (Long deviceId : deviceIds) { + String tableName = "besure_server.d_" + deviceId; + String sql = "SELECT ts FROM " + tableName + " ORDER BY ts DESC LIMIT 1"; + + try { + LocalDateTime ts = jdbcTemplate.queryForObject(sql, (rs, rowNum) -> rs.getTimestamp("ts").toLocalDateTime()); + result.put(deviceId, ts); + } catch (Exception e) { + // 表不存在或者查询失败 + result.put(deviceId, null); + log.warn("设备: {}, 获取 ts 失败: {}", deviceId, e.getMessage()); + } + } + + return result; + } + + + /** + * 查询最新的数据 + * @param deviceId + * @return + */ + @DS("tdengine") + public Map newSelectLatestRow(Long deviceId) { + + if (deviceId == null) { + return null; + } + + String tableName = "besure_server.d_" + deviceId; + + String sql = "SELECT * FROM " + tableName + " ORDER BY ts DESC LIMIT 1"; + + try { + + List> list = jdbcTemplate.queryForList(sql); + + if (list == null || list.isEmpty()) { + return null; + } + + Map row = list.get(0); + + // 转换 ts 为 LocalDateTime + Object tsObj = row.get("ts"); + if (tsObj instanceof Timestamp) { + row.put("ts", ((Timestamp) tsObj).toLocalDateTime()); + } + + log.debug("设备 {} 最新数据: {}", deviceId, row); + + return row; + + } catch (Exception e) { + + log.warn("设备 {} 查询最新数据失败: {}", deviceId, e.getMessage()); + + return null; + } + } + + + /** + * 查询指定设备在时间范围内的功率数据,并分页 + * + * @param deviceId 设备ID + * @param startTime 开始时间(字符串,格式如 "2026-02-09 00:00:00") + * @param endTime 结束时间 + * @param page 页码,从1开始 + * @param pageSize 每页条数 + * @return List> 每条记录包含 ts + 属性列 + */ + @DS("tdengine") + public List> newSelectBatchPage(Long deviceId, String startTime, String endTime, + Integer page, Integer pageSize) { + if (deviceId == null || page == null || pageSize == null) { + return Collections.emptyList(); + } + + String tableName = "besure_server.d_" + deviceId; + + // 计算分页偏移量 + int offset = (page - 1) * pageSize; + + // 构建 SQL + StringBuilder sqlBuilder = new StringBuilder(); + sqlBuilder.append("SELECT * FROM ").append(tableName) + .append(" WHERE 1=1 "); + + if (StringUtils.isNotBlank(startTime)) { + sqlBuilder.append(" AND ts >= '").append(startTime).append("' "); + } + if (StringUtils.isNotBlank(endTime)) { + sqlBuilder.append(" AND ts <= '").append(endTime).append("' "); + } + + sqlBuilder.append(" ORDER BY ts DESC ") + .append(" LIMIT ").append(pageSize) + .append(" OFFSET ").append(offset); + + String sql = sqlBuilder.toString(); + + try { + List> list = jdbcTemplate.queryForList(sql); + + // 如果 ts 需要转换为 LocalDateTime + for (Map row : list) { + Object tsObj = row.get("ts"); + if (tsObj instanceof Timestamp) { + row.put("ts", ((Timestamp) tsObj).toLocalDateTime()); + } + } + + return list; + + } catch (EmptyResultDataAccessException e) { + return Collections.emptyList(); + } catch (Exception e) { + log.error("查询设备 {} 功率数据异常", deviceId, e); + return Collections.emptyList(); + } + } + + /** + * 查询指定设备在时间范围内的数据总条数 + * + * @param deviceId 设备ID + * @param startTime 开始时间(可为空) + * @param endTime 结束时间(可为空) + * @return 总条数 + */ + @DS("tdengine") + public long newSelectBatchTotal(Long deviceId, String startTime, String endTime) { + if (deviceId == null) { + return 0; + } + + String tableName = "besure_server.d_" + deviceId; + + StringBuilder sqlBuilder = new StringBuilder(); + sqlBuilder.append("SELECT COUNT(*) AS total FROM ").append(tableName).append(" WHERE 1=1 "); + + if (StringUtils.isNotBlank(startTime)) { + sqlBuilder.append(" AND ts >= '").append(startTime).append("' "); + } + if (StringUtils.isNotBlank(endTime)) { + sqlBuilder.append(" AND ts <= '").append(endTime).append("' "); + } + + String sql = sqlBuilder.toString(); + + try { + // 查询单行结果 + Map result = jdbcTemplate.queryForMap(sql); + Object totalObj = result.get("total"); + + if (totalObj instanceof Number) { + return ((Number) totalObj).longValue(); + } else { + return 0; + } + } catch (EmptyResultDataAccessException e) { + return 0; + } catch (Exception e) { + log.error("查询设备 {} 数据总条数异常", deviceId, e); + return 0; + } + } + + + /** + * 查询指定设备在时间范围内的最新 N 条数据 + * + * @param deviceId 设备ID + * @param startTime 开始时间(可为空) + * @param endTime 结束时间(可为空) + * @param limit 查询条数 + * @return 数据列表,每条数据为 Map + */ + @DS("tdengine") + public List> newSelectLatestData(Long deviceId, String startTime, String endTime, Integer limit) { + if (deviceId == null ) { + return Collections.emptyList(); + } + + // 默认限制条数(防止全表扫描) +// int safeLimit = limit == null ? 100 : limit; + + String tableName = "besure_server.d_" + deviceId; + + StringBuilder sqlBuilder = new StringBuilder(); + sqlBuilder.append("SELECT * FROM ").append(tableName).append(" WHERE 1=1 "); + + if (StringUtils.isNotBlank(startTime)) { + sqlBuilder.append(" AND ts >= '").append(startTime).append("' "); + } + if (StringUtils.isNotBlank(endTime)) { + sqlBuilder.append(" AND ts <= '").append(endTime).append("' "); + } + + sqlBuilder.append(" ORDER BY ts DESC "); // 按时间倒序 + + if(limit !=null ){ + sqlBuilder.append(" LIMIT ").append(limit); // 限制条数 + } + + String sql = sqlBuilder.toString(); + + try { + return jdbcTemplate.queryForList(sql); + } catch (EmptyResultDataAccessException e) { + return Collections.emptyList(); + } catch (Exception e) { + log.error("查询设备 {} 最新 {} 条数据异常", deviceId, limit, e); + return Collections.emptyList(); + } + } + + + /** + * 查每个设备在时间范围内的最新一条整行数据 + * @param deviceIds + * @param startTime + * @param endTime + * @return + */ + @DS("tdengine") + public Map> queryDevicesLatestRow( + List deviceIds, + String startTime, + String endTime) { + + if (CollectionUtils.isEmpty(deviceIds)) { + return Collections.emptyMap(); + } + + Map> result = new HashMap<>(); + + for (Long deviceId : deviceIds) { + + String tableName = "besure_server.d_" + deviceId; + + StringBuilder sql = new StringBuilder(); + + sql.append("SELECT * FROM ") + .append(tableName) + .append(" WHERE 1=1 "); + + if (StringUtils.isNotBlank(startTime)) { + sql.append(" AND ts >= '").append(startTime).append("' "); + } + + if (StringUtils.isNotBlank(endTime)) { + sql.append(" AND ts <= '").append(endTime).append("' "); + } + + sql.append(" ORDER BY ts DESC LIMIT 1"); + + try { + + List> list = + jdbcTemplate.queryForList(sql.toString()); + + if (!list.isEmpty()) { + + Map row = list.get(0); + + convertTs(row); + + result.put(deviceId, row); + + } + + } catch (Exception e) { + + log.error("查询设备最新数据失败 deviceId={}", deviceId, e); + + } + } + + return result; + } + + + /** + * 只返回每个设备最新一条数据的 ts 时间 + * @param deviceIds + * @return + */ + @DS("tdengine") + public Map queryDevicesLatestTs(List deviceIds) { + + if (CollectionUtils.isEmpty(deviceIds)) { + return Collections.emptyMap(); + } + + Map result = new HashMap<>(); + + for (Long deviceId : deviceIds) { + + String sql = "SELECT LAST(ts) AS ts FROM besure_server.d_" + deviceId; + + try { + + List> list = + jdbcTemplate.queryForList(sql); + + if (!list.isEmpty() && list.get(0).get("ts") != null) { + + Timestamp ts = (Timestamp) list.get(0).get("ts"); + + result.put(deviceId, ts.toInstant()); + } + + } catch (Exception e) { + log.error("查询设备最新时间失败 deviceId={}", deviceId, e); + } + } + + return result; + } + + + + + + @DS("tdengine") + public Map> queryDevicesEarliestRow( + List deviceIds, + String startTime, + String endTime) { + + if (CollectionUtils.isEmpty(deviceIds)) { + return Collections.emptyMap(); + } + + Map> result = new HashMap<>(); + + for (Long deviceId : deviceIds) { + + String tableName = "besure_server.d_" + deviceId; + + StringBuilder sql = new StringBuilder(); + + sql.append("SELECT * FROM ") + .append(tableName) + .append(" WHERE 1=1 "); + + if (StringUtils.isNotBlank(startTime)) { + sql.append(" AND ts >= '").append(startTime).append("' "); + } + + if (StringUtils.isNotBlank(endTime)) { + sql.append(" AND ts <= '").append(endTime).append("' "); + } + + sql.append(" ORDER BY ts ASC LIMIT 1"); + + try { + + List> list = + jdbcTemplate.queryForList(sql.toString()); + + if (!list.isEmpty()) { + + Map row = list.get(0); + + convertTs(row); + + result.put(deviceId, row); + + } + + } catch (Exception e) { + + log.error("查询设备最早数据失败 deviceId={}", deviceId, e); + + } + } + + return result; + } + + + /** + * 查询近七个小时的数据 + * @param deviceIds + * @param startTime + * @param endTime + * @return + */ + @DS("tdengine") + public List> newQueryLastDataByHourBatch( + Set deviceIds, + LocalDateTime startTime, + LocalDateTime endTime) { + + if (deviceIds == null || deviceIds.isEmpty()) { + return Collections.emptyList(); + } + + List> result = new ArrayList<>(); + DateTimeFormatter fmt = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss"); + String startStr = startTime.format(fmt); + String endStr = endTime.format(fmt); + + for (Long deviceId : deviceIds) { + String tableName = "besure_server.d_" + deviceId; + + // 动态查询所有列(*),按时间倒序 + String sql = "SELECT * FROM " + tableName + + " WHERE ts >= '" + startStr + "' AND ts <= '" + endStr + "'" + + " ORDER BY ts DESC"; + + try { + List> rows = jdbcTemplate.queryForList(sql); + + // 用 map 存储每小时最新一条数据 + Map> hourMap = new HashMap<>(); + DateTimeFormatter hourFmt = DateTimeFormatter.ofPattern("yyyy-MM-dd HH"); + + for (Map row : rows) { + LocalDateTime ts = ((Timestamp) row.get("ts")).toLocalDateTime(); + String hourKey = ts.format(hourFmt); + + // 每小时只保留最新一条 + if (!hourMap.containsKey(hourKey)) { + Map map = new HashMap<>(); + map.put("deviceId", deviceId); + map.put("timestamp", ts); + + for (Map.Entry entry : row.entrySet()) { + String key = entry.getKey(); + if (!"ts".equals(key)) { + map.put(key, entry.getValue()); + } + } + + hourMap.put(hourKey, map); + } + } + + result.addAll(hourMap.values()); + + } catch (Exception e) { + log.error("查询设备 {} 数据异常", deviceId, e); + } + } + + return result; + } + + + /** + * 批量查询设备最近 N 天按天汇总数据 + * + * @param deviceIds 设备ID列表 + * @param startDate 起始日期(包含) + * @param days 查询天数 + * @return 每条记录 Map 包含 day、deviceId、timestamp、各列数据 + */ + @DS("tdengine") + public List> queryLastDataByDayBatch(Set deviceIds, LocalDate startDate, int days) { + if (deviceIds == null || deviceIds.isEmpty() || days <= 0) { + return Collections.emptyList(); + } + + List> result = new ArrayList<>(); + LocalDate endDate = startDate.plusDays(days); + + for (Long deviceId : deviceIds) { + String tableName = "besure_server.d_" + deviceId; + + String sql = String.format( + "SELECT *, ts as timestamp, DATE_FORMAT(ts, '%%Y-%%m-%%d') as day " + + "FROM %s " + + "WHERE ts >= '%s' AND ts < '%s' " + + "ORDER BY ts DESC", + tableName, startDate.toString(), endDate.toString() + ); + + try { + result.addAll(jdbcTemplate.queryForList(sql)); + } catch (Exception e) { + log.error("查询设备 {} 最近 {} 天数据异常", deviceId, days, e); + } + } + + return result; + } + + + @DS("tdengine") + public Map>> createDeviceDataMapBatch( + Set deviceIds, + Map attributeMap) { + + if (CollectionUtils.isEmpty(deviceIds)) { + return Collections.emptyMap(); + } + + Map>> result = new HashMap<>(); + + // 先把 attributeCode -> attributeId 做反向索引 + Map codeToAttrIdMap = attributeMap.values() + .stream() + .collect(Collectors.toMap( + item -> item.getAttributeCode().toLowerCase(), + DeviceContactModelDO::getId, + (a, b) -> a + )); + + for (Long deviceId : deviceIds) { + + String tableName = "besure_server.d_" + deviceId; + + String sql = "SELECT * FROM " + tableName + + " ORDER BY ts DESC LIMIT 1"; + + try { + + List> list = + jdbcTemplate.queryForList(sql); + + if (CollectionUtils.isEmpty(list)) { + continue; + } + + Map row = list.get(0); + + convertTs(row); // 你已有方法 + + Map> attributeDataMap = new HashMap<>(); + + Object ts = row.get("ts"); + + for (Map.Entry column : row.entrySet()) { + + String columnName = column.getKey(); + + if ("ts".equalsIgnoreCase(columnName)) { + continue; + } + + Long attributeId = codeToAttrIdMap.get(columnName); + if (attributeId == null) { + continue; + } + + Map valueMap = new HashMap<>(); + valueMap.put("addressValue", column.getValue()); + valueMap.put("timestamp", ts); + + attributeDataMap.put(attributeId, valueMap); + } + + result.put(deviceId, attributeDataMap); + + } catch (Exception e) { + log.error("查询设备最新数据失败 deviceId={}", deviceId, e); + } + } + + return result; + } + + + + + + private void convertTs(Map row) { + + Object tsObj = row.get("ts"); + + if (tsObj instanceof Timestamp) { + + row.put("ts", + ((Timestamp) tsObj) + .toLocalDateTime()); + } + } + + + // ========================== 运行记录表 ========================== + /** + * 插入设备运行记录到 TDengine + * @param record 设备运行记录 + * @return 是否插入成功 + */ + @DS("tdengine") + public boolean insertDeviceOperationRecord(DeviceOperationRecordDO record) { + + if (record == null || record.getDeviceId() == null) { + log.warn("设备运行记录参数为空"); + return false; + } + + // 表名 - 使用固定表名或按设备分表 + String tableName = "besure_server.iot_device_operation_record"; + // 或者按设备分表: "besure_server.operation_record_" + record.getDeviceId(); + + // 构建 SQL + StringBuilder columnBuilder = new StringBuilder(); + StringBuilder valueBuilder = new StringBuilder(); + List params = new ArrayList<>(); + + // 1. 必须字段: 时间戳 (TDengine 要求) + columnBuilder.append("ts"); + valueBuilder.append("?"); + // 使用当前时间作为 ts,或者使用 record 中的时间 +// if (record.getCreateTime() != null) { + // 将 LocalDateTime 转换为 Timestamp +// Timestamp tsTimestamp = Timestamp.valueOf(record.getCreateTime()); + params.add(new Timestamp(System.currentTimeMillis())); +// } else { +// params.add(new Timestamp(System.currentTimeMillis())); +// } + + // 2. 添加其他字段 +// addFieldIfNotNull(record.getId(), "id", columnBuilder, valueBuilder, params); + addFieldIfNotNull(record.getDeviceId(), "device_id", columnBuilder, valueBuilder, params); + addFieldIfNotNull(record.getModelId(), "model_id", columnBuilder, valueBuilder, params); + addFieldIfNotNull(record.getRule(), "rule", columnBuilder, valueBuilder, params); + addFieldIfNotNull(record.getAddressValue(), "address_value", columnBuilder, valueBuilder, params); + addFieldIfNotNull(record.getCreator(), "creator", columnBuilder, valueBuilder, params); + + // create_time 单独处理(如果已经作为 ts 使用,可以选择不重复插入) + + columnBuilder.append(", create_time"); + valueBuilder.append(", ?"); +// // 将 LocalDateTime 转换为 Timestamp +// LocalDateTime createTime = record.getCreateTime(); +// Timestamp timestamp = Timestamp.valueOf(createTime); + params.add(new Timestamp(System.currentTimeMillis())); + + + addFieldIfNotNull(record.getUpdater(), "updater", columnBuilder, valueBuilder, params); + + // update_time + + columnBuilder.append(", update_time"); + valueBuilder.append(", ?"); + params.add(new Timestamp(System.currentTimeMillis())); + + + addFieldIfNotNull(0,"deleted", columnBuilder, valueBuilder, params); + //TODO 待优化租户ID + addFieldIfNotNull("1", "tenant_id", columnBuilder, valueBuilder, params); + addFieldIfNotNull(record.getRuleId(), "rule_id", columnBuilder, valueBuilder, params); + + // 构建完整 SQL + String sql = String.format("INSERT INTO %s (%s) VALUES (%s)", + tableName, columnBuilder.toString(), valueBuilder.toString()); + + try { + int rows = jdbcTemplate.update(sql, params.toArray()); + log.info("TDengine 设备运行记录插入成功: deviceId={}, table={}, rows={}", + record.getDeviceId(), tableName, rows); + return rows > 0; + } catch (Exception e) { + log.error("TDengine 设备运行记录插入失败: deviceId={}, table={}, sql={}", + record.getDeviceId(), tableName, sql, e); + return false; + } + } + + /** + * 辅助方法:添加字段到 SQL + */ + private void addFieldIfNotNull(Object value, String columnName, + StringBuilder columnBuilder, StringBuilder valueBuilder, + List params) { + if (value != null) { + if (columnBuilder.length() > 0 && !columnBuilder.toString().endsWith("ts")) { + columnBuilder.append(", "); + valueBuilder.append(", "); + } else if (columnBuilder.length() > 0) { + columnBuilder.append(", "); + valueBuilder.append(", "); + } + columnBuilder.append(columnName); + valueBuilder.append("?"); + params.add(value); + } + } + + + /** + * 批量查询设备最新一条运行记录 + */ + @DS("tdengine") + public List selectLatestByDeviceAndRuleMinimal( + List deviceIds, + List ruleCodes) { + + if (CollectionUtils.isEmpty(deviceIds)) { + return Collections.emptyList(); + } + + // 优化:分组查询每个设备的最新记录 + List allResults = new ArrayList<>(); + + for (Long deviceId : deviceIds) { + StringBuilder sql = new StringBuilder( + "SELECT * FROM besure_server.iot_device_operation_record " + + "WHERE deleted = 0 AND device_id = ? " + ); + + List params = new ArrayList<>(); + params.add(deviceId); + + if (CollectionUtils.isNotEmpty(ruleCodes)) { + sql.append("AND rule IN ("); + for (int i = 0; i < ruleCodes.size(); i++) { + if (i > 0) sql.append(", "); + sql.append("?"); + params.add(ruleCodes.get(i)); + } + sql.append(") "); + } + + sql.append("ORDER BY create_time DESC LIMIT 1"); // 每个设备只取最新1条 + + try { + List deviceResults = jdbcTemplate.query( + sql.toString(), + params.toArray(), + (rs, rowNum) -> { + DeviceOperationRecordDO d = new DeviceOperationRecordDO(); + d.setDeviceId(rs.getLong("device_id")); + d.setModelId(rs.getLong("model_id")); + d.setRule(rs.getString("rule")); + d.setAddressValue(rs.getString("address_value")); + d.setCreator(rs.getString("creator")); + d.setCreateTime(rs.getTimestamp("create_time") != null ? rs.getTimestamp("create_time").toLocalDateTime() : null); + d.setUpdater(rs.getString("updater")); + d.setUpdateTime(rs.getTimestamp("update_time") != null ? rs.getTimestamp("update_time").toLocalDateTime() : null); + d.setDeleted(rs.getBoolean("deleted")); + d.setTenantId(String.valueOf(rs.getLong("tenant_id"))); + d.setRuleId(rs.getLong("rule_id")); + return d; + }); + + allResults.addAll(deviceResults); + + } catch (Exception e) { + log.error("查询设备{}的最新记录失败", deviceId, e); + } + + } + + return allResults; + } + + /** + * 从 TDengine 批量查询设备运行记录表最新状态 + */ + @DS("tdengine") + public Map getLatestDeviceStatusAlternative(List deviceIds) { + if (CollectionUtils.isEmpty(deviceIds)) { + return Collections.emptyMap(); + } + + Map statusMap = new HashMap<>(); + + // 分批查询,每批50个设备 + int batchSize = 50; + for (int i = 0; i < deviceIds.size(); i += batchSize) { + int end = Math.min(i + batchSize, deviceIds.size()); + List batchIds = deviceIds.subList(i, end); + + // 为每批设备构建查询 + batchIds.forEach(deviceId -> { + String status = getSingleDeviceStatus(deviceId); + if (status != null) { + statusMap.put(deviceId, status); + } + }); + } + + return statusMap; + } + + /** + * 查询单个设备的最新状态 + */ + @DS("tdengine") + private String getSingleDeviceStatus(Long deviceId) { + String sql = "SELECT rule " + + "FROM besure_server.iot_device_operation_record " + + "WHERE device_id = ? " + + " AND deleted = 0 " + + " AND rule IN ('1', '2', '3', '4') " + + "ORDER BY create_time DESC " + + "LIMIT 1"; + + try { + return jdbcTemplate.queryForObject(sql, String.class, deviceId); + } catch (EmptyResultDataAccessException e) { + // 没有记录 + return null; + } catch (Exception e) { + log.error("查询设备 {} 状态失败: {}", deviceId, e.getMessage()); + return null; + } + } + + @DS("tdengine") + public Map getDeviceTimeStatsFromTD(List deviceIds, String startTime, String endTime) { + if (CollectionUtils.isEmpty(deviceIds)) { + return Collections.emptyMap(); + } + + StringBuilder sql = new StringBuilder(); + List params = new ArrayList<>(); + + sql.append("SELECT ") + .append(" device_id, ") + .append(" SUM(CASE WHEN rule = '0' THEN 1 ELSE 0 END) * 60 AS totalOfflineTime, ") + .append(" SUM(CASE WHEN rule = '1' THEN 1 ELSE 0 END) * 60 AS totalRunningTime, ") + .append(" SUM(CASE WHEN rule = '2' THEN 1 ELSE 0 END) * 60 AS totalStandbyTime, ") + .append(" SUM(CASE WHEN rule = '3' THEN 1 ELSE 0 END) * 60 AS totalFaultTime ") + .append("FROM besure_server.iot_device_operation_record ") + .append("WHERE deleted = 0 ") + .append(" AND device_id IN ("); + + for (int i = 0; i < deviceIds.size(); i++) { + if (i > 0) sql.append(", "); + sql.append("?"); + params.add(deviceIds.get(i)); + } + sql.append(") "); + + if (startTime != null) { + sql.append(" AND create_time >= ? "); + params.add(Timestamp.valueOf(startTime)); + } + + if (endTime != null) { + sql.append(" AND create_time <= ? "); + params.add(Timestamp.valueOf(endTime)); + } + + sql.append("GROUP BY device_id"); + + try { + List> result = jdbcTemplate.queryForList( + sql.toString(), params.toArray() + ); + + return convertStatsToMap(result); + + } catch (Exception e) { + log.error("TDengine 查询设备运行时间失败", e); + return Collections.emptyMap(); + } + } + + /** + * 转换统计结果 + */ + private Map convertStatsToMap( + List> result) { + + Map statsMap = new HashMap<>(); + + for (Map row : result) { + Long deviceId = ((Number) row.get("device_id")).longValue(); + + DeviceTotalTimeRecordRespVO vo = new DeviceTotalTimeRecordRespVO(); + vo.setId(deviceId); + vo.setTotalOfflineTime(getIntValue(row, "totalofflinetime")); + vo.setTotalRunningTime(getIntValue(row, "totalrunningtime")); + vo.setTotalStandbyTime(getIntValue(row, "totalstandbytime")); + vo.setTotalFaultTime(getIntValue(row, "totalfaulttime")); + + statsMap.put(deviceId, vo); + } + + return statsMap; + } + + /** + * 安全获取整数值 + */ + private Integer getIntValue(Map map, String key) { + Object value = map.get(key); + if (value == null) return 0; + if (value instanceof Number) return ((Number) value).intValue(); + return 0; + } + + + //=============================== 告警记录表 =============================== + /** + * 插入告警记录到 TDengine(使用已有的辅助方法) + */ + @DS("tdengine") + public boolean insertDeviceWarningRecord(DeviceWarinningRecordDO deviceWarningRecordDO) { + if (deviceWarningRecordDO == null) { + log.warn("告警记录参数为空"); + return false; + } + + String tableName = "besure_server.iot_device_warning_record"; + + StringBuilder columnBuilder = new StringBuilder(); + StringBuilder valueBuilder = new StringBuilder(); + List params = new ArrayList<>(); + + // 1. 必须字段: 时间戳 + columnBuilder.append("ts"); + valueBuilder.append("?"); + params.add(new Timestamp(System.currentTimeMillis())); + + // 2. 设备信息 +// addFieldIfNotNull(deviceWarningRecordDO.getId(), "id", columnBuilder, valueBuilder, params); + addFieldIfNotNull(deviceWarningRecordDO.getDeviceId(), "device_id", columnBuilder, valueBuilder, params); + addFieldIfNotNull(deviceWarningRecordDO.getModelId(), "model_id", columnBuilder, valueBuilder, params); + + // 3. 规则和告警信息 + addFieldIfNotNull(deviceWarningRecordDO.getRule(), "rule", columnBuilder, valueBuilder, params); + addFieldIfNotNull(deviceWarningRecordDO.getAlarmLevel(), "alarm_level", columnBuilder, valueBuilder, params); + addFieldIfNotNull(deviceWarningRecordDO.getAddressValue(), "address_value", columnBuilder, valueBuilder, params); + addFieldIfNotNull(deviceWarningRecordDO.getRuleId(), "rule_id", columnBuilder, valueBuilder, params); + + // 4. 名称信息 + addFieldIfNotNull(deviceWarningRecordDO.getDeviceName(), "device_name", columnBuilder, valueBuilder, params); + addFieldIfNotNull(deviceWarningRecordDO.getModelName(), "model_name", columnBuilder, valueBuilder, params); + addFieldIfNotNull(deviceWarningRecordDO.getRuleName(), "rule_name", columnBuilder, valueBuilder, params); + + // 5. 创建者和更新者 + //TODO 待优化 + addFieldIfNotNull("1", "creator", columnBuilder, valueBuilder, params); + addFieldIfNotNull("1", "updater", columnBuilder, valueBuilder, params); + + // 6. 时间字段 + addFieldIfNotNull(new Timestamp(System.currentTimeMillis()), "create_time", columnBuilder, valueBuilder, params); + addFieldIfNotNull(new Timestamp(System.currentTimeMillis()), "update_time", columnBuilder, valueBuilder, params); + + // 7. 删除标志 + addFieldIfNotNull(0, "deleted", columnBuilder, valueBuilder, params); + + // 8. 租户ID + //TODO 待优化 + addFieldIfNotNull("1", "tenant_id", columnBuilder, valueBuilder, params); + + // 构建完整 SQL + String sql = String.format("INSERT INTO %s (%s) VALUES (%s)", + tableName, columnBuilder.toString(), valueBuilder.toString()); + + try { + int rows = jdbcTemplate.update(sql, params.toArray()); + log.info("告警记录插入成功: deviceId={}, rule={}", + deviceWarningRecordDO.getDeviceId(), + deviceWarningRecordDO.getRule()); + return rows > 0; + } catch (Exception e) { + log.error("告警记录插入失败: deviceId={}, sql={}", + deviceWarningRecordDO.getDeviceId(), sql, e); + return false; + } + } + + + + /** + * TDengine 分页查询告警记录 + */ + @DS("tdengine") + public PageResult selectRunngingRecordPage(DeviceWarinningRecordPageReqVO reqVO) { + + // 构建查询 SQL + StringBuilder sql = new StringBuilder("SELECT * FROM besure_server.iot_device_warning_record "); + StringBuilder countSql = new StringBuilder("SELECT COUNT(*) FROM besure_server.iot_device_warning_record "); + List params = new ArrayList<>(); + List countParams = new ArrayList<>(); + + // 添加 WHERE 条件 + String whereCondition = buildWhereCondition(reqVO, params); + String countWhereCondition = buildWhereCondition(reqVO, countParams); + + sql.append(whereCondition); + countSql.append(countWhereCondition); + + // 添加排序 + sql.append(" ORDER BY create_time DESC"); + + // 添加分页 + if (reqVO.getPageNo() != null && reqVO.getPageSize() != null) { + int offset = (reqVO.getPageNo() - 1) * reqVO.getPageSize(); + sql.append(" LIMIT ").append(reqVO.getPageSize()).append(" OFFSET ").append(offset); + } + + try { + // 1. 查询总数 + Long total = jdbcTemplate.queryForObject(countSql.toString(), Long.class, countParams.toArray()); + + if (total == null || total == 0) { + return new PageResult<>(Collections.emptyList(), 0L); + } + + // 2. 查询数据 + List records = jdbcTemplate.query( + sql.toString(), + params.toArray(), + (rs, rowNum) -> mapRowToDO(rs) + ); + + return new PageResult<>(records, total); + + } catch (Exception e) { + log.error("TDengine 分页查询告警记录失败", e); + return new PageResult<>(Collections.emptyList(), 0L); + } + } + + /** + * 构建 WHERE 条件 + */ + private String buildWhereCondition(DeviceWarinningRecordPageReqVO reqVO, List params) { + StringBuilder where = new StringBuilder(" WHERE deleted = 0 "); + + // 设备ID + if (reqVO.getDeviceId() != null) { + where.append(" AND device_id = ? "); + params.add(reqVO.getDeviceId()); + } + + // 模型ID + if (reqVO.getModelId() != null) { + where.append(" AND model_id = ? "); + params.add(reqVO.getModelId()); + } + + // 规则 + if (StringUtils.isNotBlank(reqVO.getRule())) { + where.append(" AND rule = ? "); + params.add(reqVO.getRule()); + } + + // 告警级别 + if (StringUtils.isNotBlank(reqVO.getAlarmLevel())) { + where.append(" AND alarm_level = ? "); + params.add(reqVO.getAlarmLevel()); + } + + // 地址值 + if (StringUtils.isNotBlank(reqVO.getAddressValue())) { + where.append(" AND address_value = ? "); + params.add(reqVO.getAddressValue()); + } + + // 创建时间范围 + if (reqVO.getCreateTime() != null && reqVO.getCreateTime().length == 2) { + LocalDateTime startTime = reqVO.getCreateTime()[0]; + LocalDateTime endTime = reqVO.getCreateTime()[1]; + + if (startTime != null) { + where.append(" AND create_time >= ? "); + params.add(Timestamp.valueOf(startTime)); + } + + if (endTime != null) { + where.append(" AND create_time <= ? "); + params.add(Timestamp.valueOf(endTime)); + } + } + + // 规则ID + if (reqVO.getRuleId() != null) { + where.append(" AND rule_id = ? "); + params.add(reqVO.getRuleId()); + } + + return where.toString(); + } + + /** + * 映射查询结果到 DO + */ + private DeviceWarinningRecordDO mapRowToDO(ResultSet rs) throws SQLException { + DeviceWarinningRecordDO record = new DeviceWarinningRecordDO(); + + // 时间戳 + Timestamp ts = rs.getTimestamp("ts"); +// if (ts != null) { +// record.setTs(ts.toLocalDateTime()); +// } + + // 其他字段 +// record.setId(rs.getLong("id")); + record.setDeviceId(rs.getLong("device_id")); + record.setModelId(rs.getLong("model_id")); + record.setRule(rs.getString("rule")); + record.setAlarmLevel(rs.getString("alarm_level")); + record.setAddressValue(rs.getString("address_value")); + record.setCreator(rs.getString("creator")); + + Timestamp createTime = rs.getTimestamp("create_time"); + if (createTime != null) { + record.setCreateTime(createTime.toLocalDateTime()); + } + + record.setUpdater(rs.getString("updater")); + + Timestamp updateTime = rs.getTimestamp("update_time"); + if (updateTime != null) { + record.setUpdateTime(updateTime.toLocalDateTime()); + } + + record.setDeleted(rs.getInt("deleted") == 1); +// record.setTenantId(rs.getLong("tenant_id")); + record.setRuleId(rs.getLong("rule_id")); + record.setDeviceName(rs.getString("device_name")); + record.setModelName(rs.getString("model_name")); + record.setRuleName(rs.getString("rule_name")); + + return record; + } + + + + /** + * 查询设备告警记录列表(限制100条) + */ + @DS("tdengine") + public List selectDeviceWarningList(Long deviceId) { + + StringBuilder sql = new StringBuilder(); + List params = new ArrayList<>(); + + sql.append("SELECT * FROM besure_server.iot_device_warning_record ") + .append("WHERE deleted = 0 "); + + if (deviceId != null) { + sql.append("AND device_id = ? "); + params.add(deviceId); + } + + sql.append("ORDER BY create_time DESC ") + .append("LIMIT 100"); + + try { + return jdbcTemplate.query(sql.toString(), params.toArray(), (rs, rowNum) -> { + return mapRowToWarningRecordDO(rs); + }); + } catch (Exception e) { + log.error("查询设备告警记录失败: deviceId={}", deviceId, e); + return Collections.emptyList(); + } + } + + /** + * 映射查询结果 + */ + private DeviceWarinningRecordDO mapRowToWarningRecordDO(ResultSet rs) throws SQLException { + DeviceWarinningRecordDO record = new DeviceWarinningRecordDO(); + + // 时间戳 + Timestamp ts = rs.getTimestamp("ts"); +// if (ts != null) { +// record.setTs(ts.toLocalDateTime()); +// } + + // 其他字段 +// record.setId(rs.getLong("id")); + record.setDeviceId(rs.getLong("device_id")); + record.setModelId(rs.getLong("model_id")); + record.setRule(rs.getString("rule")); + record.setAlarmLevel(rs.getString("alarm_level")); + record.setAddressValue(rs.getString("address_value")); + record.setCreator(rs.getString("creator")); + + Timestamp createTime = rs.getTimestamp("create_time"); + if (createTime != null) { + record.setCreateTime(createTime.toLocalDateTime()); + } + + record.setUpdater(rs.getString("updater")); + + Timestamp updateTime = rs.getTimestamp("update_time"); + if (updateTime != null) { + record.setUpdateTime(updateTime.toLocalDateTime()); + } + + record.setDeleted(rs.getInt("deleted") == 1); +// record.setTenantId(rs.getLong("tenant_id")); + record.setRuleId(rs.getLong("rule_id")); + record.setDeviceName(rs.getString("device_name")); + record.setModelName(rs.getString("model_name")); + record.setRuleName(rs.getString("rule_name")); + + return record; + } + } \ No newline at end of file diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/device/message/IotDeviceMessageService.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/device/message/IotDeviceMessageService.java deleted file mode 100644 index dc1224b..0000000 --- a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/device/message/IotDeviceMessageService.java +++ /dev/null @@ -1,97 +0,0 @@ -package cn.iocoder.yudao.module.iot.service.device.message; - -import cn.iocoder.yudao.framework.common.pojo.PageResult; -import cn.iocoder.yudao.module.iot.controller.admin.device.vo.message.IotDeviceMessagePageReqVO; -import cn.iocoder.yudao.module.iot.controller.admin.statistics.vo.IotStatisticsDeviceMessageReqVO; -import cn.iocoder.yudao.module.iot.controller.admin.statistics.vo.IotStatisticsDeviceMessageSummaryByDateRespVO; -import cn.iocoder.yudao.module.iot.core.mq.message.IotDeviceMessage; -import cn.iocoder.yudao.module.iot.dal.dataobject.device.IotDeviceDO; -import cn.iocoder.yudao.module.iot.dal.dataobject.device.IotDeviceMessageDO; - -import javax.annotation.Nullable; -import javax.validation.constraints.NotNull; -import java.time.LocalDateTime; -import java.util.List; - -/** - * IoT 设备消息 Service 接口 - * - * @author 芋道源码 - */ -public interface IotDeviceMessageService { - - /** - * 初始化设备消息的 TDengine 超级表 - * - * 系统启动时,会自动初始化一次 - */ - void defineDeviceMessageStable(); - - /** - * 发送设备消息 - * - * @param message 消息(“codec(编解码)字段” 部分字段) - * @param device 设备 - * @return 设备消息 - */ - IotDeviceMessage sendDeviceMessage(IotDeviceMessage message, IotDeviceDO device); - - /** - * 发送设备消息 - * - * @param message 消息(“codec(编解码)字段” 部分字段) - * @return 设备消息 - */ - IotDeviceMessage sendDeviceMessage(IotDeviceMessage message); - - /** - * 处理设备上行的消息,包括如下步骤: - * - * 1. 处理消息 - * 2. 记录消息 - * 3. 回复消息 - * - * @param message 消息 - * @param device 设备 - */ - void handleUpstreamDeviceMessage(IotDeviceMessage message, IotDeviceDO device); - - /** - * 获得设备消息分页 - * - * @param pageReqVO 分页查询 - * @return 设备消息分页 - */ - PageResult getDeviceMessagePage(IotDeviceMessagePageReqVO pageReqVO); - - /** - * 获得指定 requestId 的设备消息列表 - * - * @param deviceId 设备编号 - * @param requestIds requestId 列表 - * @param reply 是否回复 - * @return 设备消息列表 - */ - List getDeviceMessageListByRequestIdsAndReply( - @NotNull(message = "设备编号不能为空") Long deviceId, - List requestIds, - Boolean reply); - - /** - * 获得设备消息数量 - * - * @param createTime 创建时间,如果为空,则统计所有消息数量 - * @return 消息数量 - */ - Long getDeviceMessageCount(@Nullable LocalDateTime createTime); - - /** - * 获取设备消息的数据统计 - * - * @param reqVO 统计请求 - * @return 设备消息的数据统计 - */ - List getDeviceMessageSummaryByDate( - IotStatisticsDeviceMessageReqVO reqVO); - -} diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/device/message/IotDeviceMessageServiceImpl.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/device/message/IotDeviceMessageServiceImpl.java deleted file mode 100644 index 5811929..0000000 --- a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/device/message/IotDeviceMessageServiceImpl.java +++ /dev/null @@ -1,380 +0,0 @@ -package cn.iocoder.yudao.module.iot.service.device.message; - -import cn.hutool.core.collection.CollUtil; -import cn.hutool.core.collection.ListUtil; -import cn.hutool.core.date.LocalDateTimeUtil; -import cn.hutool.core.lang.Assert; -import cn.hutool.core.map.MapUtil; -import cn.hutool.core.util.StrUtil; -import cn.hutool.extra.spring.SpringUtil; -import cn.iocoder.yudao.framework.common.exception.ServiceException; -import cn.iocoder.yudao.framework.common.pojo.PageResult; -import cn.iocoder.yudao.framework.common.util.date.LocalDateTimeUtils; -import cn.iocoder.yudao.framework.common.util.json.JsonUtils; -import cn.iocoder.yudao.framework.common.util.object.BeanUtils; -import cn.iocoder.yudao.module.iot.controller.admin.device.vo.message.IotDeviceMessagePageReqVO; -import cn.iocoder.yudao.module.iot.controller.admin.statistics.vo.IotStatisticsDeviceMessageReqVO; -import cn.iocoder.yudao.module.iot.controller.admin.statistics.vo.IotStatisticsDeviceMessageSummaryByDateRespVO; -import cn.iocoder.yudao.module.iot.core.enums.IotDeviceMessageMethodEnum; -import cn.iocoder.yudao.module.iot.core.mq.message.IotDeviceMessage; -import cn.iocoder.yudao.module.iot.core.mq.producer.IotDeviceMessageProducer; -import cn.iocoder.yudao.module.iot.core.topic.IotDeviceIdentity; -import cn.iocoder.yudao.module.iot.core.topic.event.IotDeviceEventPostReqDTO; -import cn.iocoder.yudao.module.iot.core.topic.property.IotDevicePropertyPackPostReqDTO; -import cn.iocoder.yudao.module.iot.core.topic.property.IotDevicePropertyPostReqDTO; -import cn.iocoder.yudao.module.iot.core.util.IotDeviceMessageUtils; -import cn.iocoder.yudao.module.iot.dal.dataobject.device.IotDeviceDO; -import cn.iocoder.yudao.module.iot.dal.dataobject.device.IotDeviceMessageDO; -import cn.iocoder.yudao.module.iot.dal.tdengine.IotDeviceMessageMapper; -import cn.iocoder.yudao.module.iot.service.device.IotDeviceService; -import cn.iocoder.yudao.module.iot.service.device.property.IotDevicePropertyService; -import cn.iocoder.yudao.module.iot.service.ota.IotOtaTaskRecordService; -import com.baomidou.mybatisplus.core.metadata.IPage; -import com.baomidou.mybatisplus.extension.plugins.pagination.Page; -import com.google.common.base.Objects; -import lombok.extern.slf4j.Slf4j; -import org.springframework.context.annotation.Lazy; -import org.springframework.scheduling.annotation.Async; -import org.springframework.stereotype.Service; -import org.springframework.validation.annotation.Validated; - -import javax.annotation.Resource; -import java.sql.Timestamp; -import java.time.LocalDateTime; -import java.util.List; -import java.util.Map; - -import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception; -import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertList; -import static cn.iocoder.yudao.module.iot.enums.ErrorCodeConstants.DEVICE_DOWNSTREAM_FAILED_SERVER_ID_NULL; - -/** - * IoT 设备消息 Service 实现类 - * - * @author 芋道源码 - */ -@Service -@Validated -@Slf4j -public class IotDeviceMessageServiceImpl implements IotDeviceMessageService { - - @Resource - private IotDeviceService deviceService; - @Resource - private IotDevicePropertyService devicePropertyService; - @Resource - @Lazy // 延迟加载,避免循环依赖 - private IotOtaTaskRecordService otaTaskRecordService; - - @Resource - private IotDeviceMessageMapper deviceMessageMapper; - - @Resource - private IotDeviceMessageProducer deviceMessageProducer; - - @Override - public void defineDeviceMessageStable() { - if (StrUtil.isNotEmpty(deviceMessageMapper.showSTable())) { - log.info("[defineDeviceMessageStable][设备消息超级表已存在,创建跳过]"); - return; - } - log.info("[defineDeviceMessageStable][设备消息超级表不存在,创建开始...]"); - deviceMessageMapper.createSTable(); - log.info("[defineDeviceMessageStable][设备消息超级表不存在,创建成功]"); - } - - @Async - void createDeviceLogAsync(IotDeviceMessage message) { - IotDeviceMessageDO messageDO = BeanUtils.toBean(message, IotDeviceMessageDO.class) - .setUpstream(IotDeviceMessageUtils.isUpstreamMessage(message)) - .setReply(IotDeviceMessageUtils.isReplyMessage(message)) - .setIdentifier(IotDeviceMessageUtils.getIdentifier(message)); - if (message.getParams() != null) { - messageDO.setParams(JsonUtils.toJsonString(messageDO.getParams())); - } - if (messageDO.getData() != null) { - messageDO.setData(JsonUtils.toJsonString(messageDO.getData())); - } - deviceMessageMapper.insert(messageDO); - } - - @Override - public IotDeviceMessage sendDeviceMessage(IotDeviceMessage message) { - IotDeviceDO device = deviceService.validateDeviceExists(message.getDeviceId()); - return sendDeviceMessage(message, device); - } - - @Override - public IotDeviceMessage sendDeviceMessage(IotDeviceMessage message, IotDeviceDO device) { - return sendDeviceMessage(message, device, null); - } - - private IotDeviceMessage sendDeviceMessage(IotDeviceMessage message, IotDeviceDO device, String serverId) { - // 1. 补充信息 - appendDeviceMessage(message, device); - - // 2.1 情况一:发送上行消息 - boolean upstream = IotDeviceMessageUtils.isUpstreamMessage(message); - if (upstream) { - deviceMessageProducer.sendDeviceMessage(message); - return message; - } - - // 2.2 情况二:发送下行消息 - // 如果是下行消息,需要校验 serverId 存在 - // TODO 芋艿:【设计】下行消息需要区分 PUSH 和 PULL 模型 - // 1. PUSH 模型:适用于 MQTT 等长连接协议。通过 serverId 将消息路由到指定网关,实时推送。 - // 2. PULL 模型:适用于 HTTP 等短连接协议。设备无固定 serverId,无法主动推送。 - // 解决方案: - // 当 serverId 不存在时,将下行消息存入“待拉取消息表”(例如 iot_device_pull_message)。 - // 设备端通过定时轮询一个新增的 API(例如 /iot/message/pull)来拉取属于自己的消息。 - if (StrUtil.isEmpty(serverId)) { - serverId = devicePropertyService.getDeviceServerId(device.getId()); - if (StrUtil.isEmpty(serverId)) { - throw exception(DEVICE_DOWNSTREAM_FAILED_SERVER_ID_NULL); - } - } - deviceMessageProducer.sendDeviceMessageToGateway(serverId, message); - // 特殊:记录消息日志。原因:上行消息,消费时,已经会记录;下行消息,因为消费在 Gateway 端,所以需要在这里记录 - getSelf().createDeviceLogAsync(message); - return message; - } - - /** - * 补充消息的后端字段 - * - * @param message 消息 - * @param device 设备信息 - */ - private void appendDeviceMessage(IotDeviceMessage message, IotDeviceDO device) { - message.setId(IotDeviceMessageUtils.generateMessageId()).setReportTime(LocalDateTime.now()) - .setDeviceId(device.getId()).setTenantId(device.getTenantId()); - // 特殊:如果设备没有指定 requestId,则使用 messageId - if (StrUtil.isEmpty(message.getRequestId())) { - message.setRequestId(message.getId()); - } - } - - @Override - public void handleUpstreamDeviceMessage(IotDeviceMessage message, IotDeviceDO device) { - // 1. 处理消息 - Object replyData = null; - ServiceException serviceException = null; - try { - replyData = handleUpstreamDeviceMessage0(message, device); - } catch (ServiceException ex) { - serviceException = ex; - log.warn("[handleUpstreamDeviceMessage][message({}) 业务异常]", message, serviceException); - } catch (Exception ex) { - log.error("[handleUpstreamDeviceMessage][message({}) 发生异常]", message, ex); - throw ex; - } - - // 2. 记录消息 - getSelf().createDeviceLogAsync(message); - - // 3. 回复消息。前提:非 _reply 消息、非禁用回复的消息 - if (IotDeviceMessageUtils.isReplyMessage(message) - || IotDeviceMessageMethodEnum.isReplyDisabled(message.getMethod()) - || StrUtil.isEmpty(message.getServerId())) { - return; - } - try { - IotDeviceMessage replyMessage = IotDeviceMessage.replyOf(message.getRequestId(), message.getMethod(), replyData, - serviceException != null ? serviceException.getCode() : null, - serviceException != null ? serviceException.getMessage() : null); - sendDeviceMessage(replyMessage, device, message.getServerId()); - } catch (Exception ex) { - log.error("[handleUpstreamDeviceMessage][message({}) 回复消息失败]", message, ex); - } - } - - // TODO @芋艿:可优化:未来逻辑复杂后,可以独立拆除 Processor 处理器 - private Object handleUpstreamDeviceMessage0(IotDeviceMessage message, IotDeviceDO device) { - // 设备上下线 - if (Objects.equal(message.getMethod(), IotDeviceMessageMethodEnum.STATE_UPDATE.getMethod())) { - String stateStr = IotDeviceMessageUtils.getIdentifier(message); - assert stateStr != null; - Assert.notEmpty(stateStr, "设备状态不能为空"); - Integer state = Integer.valueOf(stateStr); - deviceService.updateDeviceState(device, state); - return null; - } - - // 属性上报 - if (Objects.equal(message.getMethod(), IotDeviceMessageMethodEnum.PROPERTY_POST.getMethod())) { - devicePropertyService.saveDeviceProperty(device, message); - return null; - } - // 批量上报(属性+事件+子设备) - if (Objects.equal(message.getMethod(), IotDeviceMessageMethodEnum.PROPERTY_PACK_POST.getMethod())) { - handlePackMessage(message, device); - return null; - } - - // OTA 上报升级进度 - if (Objects.equal(message.getMethod(), IotDeviceMessageMethodEnum.OTA_PROGRESS.getMethod())) { - otaTaskRecordService.updateOtaRecordProgress(device, message); - return null; - } - - // 添加拓扑关系 - if (Objects.equal(message.getMethod(), IotDeviceMessageMethodEnum.TOPO_ADD.getMethod())) { - return deviceService.handleTopoAddMessage(message, device); - } - // 删除拓扑关系 - if (Objects.equal(message.getMethod(), IotDeviceMessageMethodEnum.TOPO_DELETE.getMethod())) { - return deviceService.handleTopoDeleteMessage(message, device); - } - // 获取拓扑关系 - if (Objects.equal(message.getMethod(), IotDeviceMessageMethodEnum.TOPO_GET.getMethod())) { - return deviceService.handleTopoGetMessage(device); - } - - // 子设备动态注册 - if (Objects.equal(message.getMethod(), IotDeviceMessageMethodEnum.SUB_DEVICE_REGISTER.getMethod())) { - return deviceService.handleSubDeviceRegisterMessage(message, device); - } - - return null; - } - - // ========== 批量上报处理方法 ========== - - /** - * 处理批量上报消息 - *

- * 将 pack 消息拆分成多条标准消息,发送到 MQ 让规则引擎处理 - * - * @param packMessage 批量消息 - * @param gatewayDevice 网关设备 - */ - private void handlePackMessage(IotDeviceMessage packMessage, IotDeviceDO gatewayDevice) { - // 1. 解析参数 - IotDevicePropertyPackPostReqDTO params = JsonUtils.convertObject( - packMessage.getParams(), IotDevicePropertyPackPostReqDTO.class); - if (params == null) { - log.warn("[handlePackMessage][消息({}) 参数解析失败]", packMessage); - return; - } - - // 2. 处理网关设备(自身)的数据 - sendDevicePackData(gatewayDevice, packMessage.getServerId(), params.getProperties(), params.getEvents()); - - // 3. 处理子设备的数据 - if (CollUtil.isEmpty(params.getSubDevices())) { - return; - } - for (IotDevicePropertyPackPostReqDTO.SubDeviceData subDeviceData : params.getSubDevices()) { - try { - IotDeviceIdentity identity = subDeviceData.getIdentity(); - IotDeviceDO subDevice = deviceService.getDeviceFromCache(identity.getProductKey(), identity.getDeviceName()); - if (subDevice == null) { - log.warn("[handlePackMessage][子设备({}/{}) 不存在]", identity.getProductKey(), identity.getDeviceName()); - continue; - } - // 特殊:子设备不需要指定 serverId,因为子设备实际可能连接在不同的 gateway-server 上,导致 serverId 不同 - sendDevicePackData(subDevice, null, subDeviceData.getProperties(), subDeviceData.getEvents()); - } catch (Exception ex) { - log.error("[handlePackMessage][子设备({}/{}) 数据处理失败]", subDeviceData.getIdentity().getProductKey(), - subDeviceData.getIdentity().getDeviceName(), ex); - } - } - } - - /** - * 发送设备 pack 数据到 MQ(属性 + 事件) - * - * @param device 设备 - * @param serverId 服务标识 - * @param properties 属性数据 - * @param events 事件数据 - */ - private void sendDevicePackData(IotDeviceDO device, String serverId, - Map properties, - Map events) { - // 1. 发送属性消息 - if (MapUtil.isNotEmpty(properties)) { - IotDeviceMessage propertyMsg = IotDeviceMessage.requestOf( - device.getId(), device.getTenantId(), serverId, - IotDeviceMessageMethodEnum.PROPERTY_POST.getMethod(), - IotDevicePropertyPostReqDTO.of(properties)); - deviceMessageProducer.sendDeviceMessage(propertyMsg); - } - - // 2. 发送事件消息 - if (MapUtil.isNotEmpty(events)) { - for (Map.Entry eventEntry : events.entrySet()) { - String eventId = eventEntry.getKey(); - IotDevicePropertyPackPostReqDTO.EventValue eventValue = eventEntry.getValue(); - if (eventValue == null) { - continue; - } - IotDeviceMessage eventMsg = IotDeviceMessage.requestOf( - device.getId(), device.getTenantId(), serverId, - IotDeviceMessageMethodEnum.EVENT_POST.getMethod(), - IotDeviceEventPostReqDTO.of(eventId, eventValue.getValue(), eventValue.getTime())); - deviceMessageProducer.sendDeviceMessage(eventMsg); - } - } - } - - // ========= 设备消息查询 ========== - - @Override - public PageResult getDeviceMessagePage(IotDeviceMessagePageReqVO pageReqVO) { - try { - IPage page = deviceMessageMapper.selectPage( - new Page<>(pageReqVO.getPageNo(), pageReqVO.getPageSize()), pageReqVO); - return new PageResult<>(page.getRecords(), page.getTotal()); - } catch (Exception exception) { - if (exception.getMessage().contains("Table does not exist")) { - return PageResult.empty(); - } - throw exception; - } - } - - @Override - public List getDeviceMessageListByRequestIdsAndReply(Long deviceId, List requestIds, Boolean reply) { - if (CollUtil.isEmpty(requestIds)) { - return ListUtil.of(); - } - return deviceMessageMapper.selectListByRequestIdsAndReply(deviceId, requestIds, reply); - } - - @Override - public Long getDeviceMessageCount(LocalDateTime createTime) { - return deviceMessageMapper.selectCountByCreateTime( - createTime != null ? LocalDateTimeUtil.toEpochMilli(createTime) : null); - } - - @Override - public List getDeviceMessageSummaryByDate( - IotStatisticsDeviceMessageReqVO reqVO) { - // 1. 按小时统计,获取分项统计数据 - List> countList = deviceMessageMapper.selectDeviceMessageCountGroupByDate( - LocalDateTimeUtil.toEpochMilli(reqVO.getTimes()[0]), - LocalDateTimeUtil.toEpochMilli(reqVO.getTimes()[1])); - - // 2. 按照日期间隔,合并数据 - List timeRanges = LocalDateTimeUtils.getDateRangeList(reqVO.getTimes()[0], reqVO.getTimes()[1], - reqVO.getInterval()); - return convertList(timeRanges, times -> { - Integer upstreamCount = countList.stream() - .filter(vo -> LocalDateTimeUtils.isBetween(times[0], times[1], (Timestamp) vo.get("time"))) - .mapToInt(value -> MapUtil.getInt(value, "upstream_count")).sum(); - Integer downstreamCount = countList.stream() - .filter(vo -> LocalDateTimeUtils.isBetween(times[0], times[1], (Timestamp) vo.get("time"))) - .mapToInt(value -> MapUtil.getInt(value, "downstream_count")).sum(); - return new IotStatisticsDeviceMessageSummaryByDateRespVO() - .setTime(LocalDateTimeUtils.formatDateRange(times[0], times[1], reqVO.getInterval())) - .setUpstreamCount(upstreamCount).setDownstreamCount(downstreamCount); - }); - } - - private IotDeviceMessageServiceImpl getSelf() { - return SpringUtil.getBean(getClass()); - } - -} diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/device/property/IotDevicePropertyService.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/device/property/IotDevicePropertyService.java deleted file mode 100644 index 80754cc..0000000 --- a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/device/property/IotDevicePropertyService.java +++ /dev/null @@ -1,89 +0,0 @@ -package cn.iocoder.yudao.module.iot.service.device.property; - -import cn.iocoder.yudao.module.iot.controller.admin.device.vo.property.IotDevicePropertyHistoryListReqVO; -import cn.iocoder.yudao.module.iot.controller.admin.device.vo.property.IotDevicePropertyRespVO; -import cn.iocoder.yudao.module.iot.core.mq.message.IotDeviceMessage; -import cn.iocoder.yudao.module.iot.dal.dataobject.device.IotDeviceDO; -import cn.iocoder.yudao.module.iot.dal.dataobject.device.IotDevicePropertyDO; - -import javax.validation.Valid; -import java.time.LocalDateTime; -import java.util.List; -import java.util.Map; -import java.util.Set; - -/** - * IoT 设备【属性】数据 Service 接口 - * - * @author 芋道源码 - */ -public interface IotDevicePropertyService { - - // ========== 设备属性相关操作 ========== - - /** - * 定义设备属性数据的结构 - * - * @param productId 产品编号 - */ - void defineDevicePropertyData(Long productId); - - /** - * 保存设备数据 - * - * @param device 设备 - * @param message 设备消息 - */ - void saveDeviceProperty(IotDeviceDO device, IotDeviceMessage message); - - /** - * 获得设备属性最新数据 - * - * @param deviceId 设备编号 - * @return 设备属性最新数据 - */ - Map getLatestDeviceProperties(Long deviceId); - - /** - * 获得设备属性历史数据 - * - * @param listReqVO 列表请求 - * @return 设备属性历史数据 - */ - List getHistoryDevicePropertyList(@Valid IotDevicePropertyHistoryListReqVO listReqVO); - - // ========== 设备时间相关操作 ========== - - /** - * 获得最后上报时间小于指定时间的设备编号集合 - * - * @param maxReportTime 最大上报时间 - * @return 设备编号集合 - */ - Set getDeviceIdListByReportTime(LocalDateTime maxReportTime); - - /** - * 更新设备上报时间 - * - * @param id 设备编号 - * @param reportTime 上报时间 - */ - void updateDeviceReportTimeAsync(Long id, LocalDateTime reportTime); - - /** - * 更新设备关联的网关服务 serverId - * - * @param id 设备编号 - * @param serverId 网关 serverId - */ - void updateDeviceServerIdAsync(Long id, String serverId); - - /** - * 获得设备关联的网关服务 serverId - * - * @param id 设备编号 - * @return 网关 serverId - */ - String getDeviceServerId(Long id); - -} \ No newline at end of file diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/device/property/IotDevicePropertyServiceImpl.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/device/property/IotDevicePropertyServiceImpl.java deleted file mode 100644 index 39b71a5..0000000 --- a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/device/property/IotDevicePropertyServiceImpl.java +++ /dev/null @@ -1,307 +0,0 @@ -package cn.iocoder.yudao.module.iot.service.device.property; - -import cn.hutool.core.collection.CollUtil; -import cn.hutool.core.convert.Convert; -import cn.hutool.core.date.LocalDateTimeUtil; -import cn.hutool.core.map.MapUtil; -import cn.hutool.core.util.StrUtil; -import cn.iocoder.yudao.framework.common.util.json.JsonUtils; -import cn.iocoder.yudao.framework.common.util.object.ObjectUtils; -import cn.iocoder.yudao.module.iot.controller.admin.device.vo.property.IotDevicePropertyHistoryListReqVO; -import cn.iocoder.yudao.module.iot.controller.admin.device.vo.property.IotDevicePropertyRespVO; -import cn.iocoder.yudao.module.iot.core.mq.message.IotDeviceMessage; -import cn.iocoder.yudao.module.iot.dal.dataobject.device.IotDeviceDO; -import cn.iocoder.yudao.module.iot.dal.dataobject.device.IotDevicePropertyDO; -import cn.iocoder.yudao.module.iot.dal.dataobject.product.IotProductDO; -import cn.iocoder.yudao.module.iot.dal.dataobject.thingmodel.IotThingModelDO; -import cn.iocoder.yudao.module.iot.dal.dataobject.thingmodel.model.dataType.ThingModelDateOrTextDataSpecs; -import cn.iocoder.yudao.module.iot.dal.redis.device.DevicePropertyRedisDAO; -import cn.iocoder.yudao.module.iot.dal.redis.device.DeviceReportTimeRedisDAO; -import cn.iocoder.yudao.module.iot.dal.redis.device.DeviceServerIdRedisDAO; -import cn.iocoder.yudao.module.iot.dal.tdengine.IotDevicePropertyMapper; -import cn.iocoder.yudao.module.iot.enums.thingmodel.IotDataSpecsDataTypeEnum; -import cn.iocoder.yudao.module.iot.enums.thingmodel.IotThingModelTypeEnum; -import cn.iocoder.yudao.module.iot.framework.tdengine.core.TDengineTableField; -import cn.iocoder.yudao.module.iot.service.device.IotDeviceService; -import cn.iocoder.yudao.module.iot.service.product.IotProductService; -import cn.iocoder.yudao.module.iot.service.thingmodel.IotThingModelService; -import lombok.extern.slf4j.Slf4j; -import org.springframework.context.annotation.Lazy; -import org.springframework.scheduling.annotation.Async; -import org.springframework.stereotype.Service; - -import javax.annotation.Resource; -import java.math.BigDecimal; -import java.time.LocalDateTime; -import java.util.*; - -import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.*; -import static cn.iocoder.yudao.framework.common.util.collection.MapUtils.getBigDecimal; - -/** - * IoT 设备【属性】数据 Service 实现类 - * - * @author 芋道源码 - */ -@Service -@Slf4j -public class IotDevicePropertyServiceImpl implements IotDevicePropertyService { - - /** - * 物模型的数据类型,与 TDengine 数据类型的映射关系 - * - * @see TDEngine 数据类型 - */ - private static final Map TYPE_MAPPING = MapUtil.builder() - .put(IotDataSpecsDataTypeEnum.INT.getDataType(), TDengineTableField.TYPE_INT) - .put(IotDataSpecsDataTypeEnum.FLOAT.getDataType(), TDengineTableField.TYPE_FLOAT) - .put(IotDataSpecsDataTypeEnum.DOUBLE.getDataType(), TDengineTableField.TYPE_DOUBLE) - .put(IotDataSpecsDataTypeEnum.ENUM.getDataType(), TDengineTableField.TYPE_TINYINT) - .put(IotDataSpecsDataTypeEnum.BOOL.getDataType(), TDengineTableField.TYPE_TINYINT) - .put(IotDataSpecsDataTypeEnum.TEXT.getDataType(), TDengineTableField.TYPE_VARCHAR) - .put(IotDataSpecsDataTypeEnum.DATE.getDataType(), TDengineTableField.TYPE_TIMESTAMP) - .put(IotDataSpecsDataTypeEnum.STRUCT.getDataType(), TDengineTableField.TYPE_VARCHAR) - .put(IotDataSpecsDataTypeEnum.ARRAY.getDataType(), TDengineTableField.TYPE_VARCHAR) - .build(); - - @Resource - private IotThingModelService thingModelService; - @Resource - @Lazy // 延迟加载,解决循环依赖 - private IotProductService productService; - @Resource - @Lazy // 延迟加载,解决循环依赖 - private IotDeviceService deviceService; - - @Resource - private DevicePropertyRedisDAO deviceDataRedisDAO; - @Resource - private DeviceReportTimeRedisDAO deviceReportTimeRedisDAO; - @Resource - private DeviceServerIdRedisDAO deviceServerIdRedisDAO; - - @Resource - private IotDevicePropertyMapper devicePropertyMapper; - - // ========== 设备属性相关操作 ========== - - @Override - public void defineDevicePropertyData(Long productId) { - // 1.1 查询产品和物模型 - IotProductDO product = productService.validateProductExists(productId); - List thingModels = filterList(thingModelService.getThingModelListByProductId(productId), - thingModel -> IotThingModelTypeEnum.PROPERTY.getType().equals(thingModel.getType())); - // 1.2 解析 DB 里的字段 - List oldFields = new ArrayList<>(); - try { - oldFields.addAll(devicePropertyMapper.getProductPropertySTableFieldList(product.getId())); - } catch (Exception e) { - if (!e.getMessage().contains("Table does not exist")) { - throw e; - } - } - - // 2.1 情况一:如果是新增的时候,需要创建表 - List newFields = buildTableFieldList(thingModels); - if (CollUtil.isEmpty(oldFields)) { - if (CollUtil.isEmpty(newFields)) { - log.info("[defineDevicePropertyData][productId({}) 没有需要定义的属性]", productId); - return; - } - devicePropertyMapper.createProductPropertySTable(product.getId(), newFields); - return; - } - // 2.2 情况二:如果是修改的时候,需要更新表 - devicePropertyMapper.alterProductPropertySTable(product.getId(), oldFields, newFields); - } - - private List buildTableFieldList(List thingModels) { - return convertList(thingModels, thingModel -> { - TDengineTableField field = new TDengineTableField( - StrUtil.toUnderlineCase(thingModel.getIdentifier()), // TDengine 字段默认都是小写 - TYPE_MAPPING.get(thingModel.getProperty().getDataType())); - String dataType = thingModel.getProperty().getDataType(); - if (Objects.equals(dataType, IotDataSpecsDataTypeEnum.TEXT.getDataType())) { - field.setLength(((ThingModelDateOrTextDataSpecs) thingModel.getProperty().getDataSpecs()).getLength()); - } else if (ObjectUtils.equalsAny(dataType, IotDataSpecsDataTypeEnum.STRUCT.getDataType(), - IotDataSpecsDataTypeEnum.ARRAY.getDataType())) { - field.setLength(TDengineTableField.LENGTH_VARCHAR); - } - return field; - }); - } - - @Override - @SuppressWarnings("PatternVariableCanBeUsed") - public void saveDeviceProperty(IotDeviceDO device, IotDeviceMessage message) { - if (!(message.getParams() instanceof Map)) { - log.error("[saveDeviceProperty][消息内容({}) 的 data 类型不正确]", message); - return; - } - Map params = (Map) message.getParams(); - if (CollUtil.isEmpty(params)) { - log.error("[saveDeviceProperty][消息内容({}) 的 data 为空]", message); - return; - } - - // 1. 根据物模型,拼接合法的属性 - // TODO @芋艿:【待定 004】赋能后,属性到底以 thingModel 为准(ik),还是 db 的表结构为准(tl)? - List thingModels = thingModelService.getThingModelListByProductIdFromCache(device.getProductId()); - Map properties = new HashMap<>(); - params.forEach((key, value) -> { - IotThingModelDO thingModel = CollUtil.findOne(thingModels, o -> o.getIdentifier().equals(key)); - if (thingModel == null || thingModel.getProperty() == null) { - log.error("[saveDeviceProperty][消息({}) 的属性({}) 不存在]", message, key); - return; - } - String dataType = thingModel.getProperty().getDataType(); - if (ObjectUtils.equalsAny(dataType, - IotDataSpecsDataTypeEnum.STRUCT.getDataType(), IotDataSpecsDataTypeEnum.ARRAY.getDataType())) { - // 特殊:STRUCT 和 ARRAY 类型,在 TDengine 里,有没对应数据类型,只能通过 JSON 来存储 - properties.put((String) key, JsonUtils.toJsonString(value)); - } else if (IotDataSpecsDataTypeEnum.INT.getDataType().equals(dataType)) { - properties.put((String) key, Convert.toInt(value)); - } else if (IotDataSpecsDataTypeEnum.FLOAT.getDataType().equals(dataType)) { - properties.put((String) key, Convert.toFloat(value)); - } else if (IotDataSpecsDataTypeEnum.DOUBLE.getDataType().equals(dataType)) { - properties.put((String) key, Convert.toDouble(value)); - } else if (IotDataSpecsDataTypeEnum.BOOL.getDataType().equals(dataType)) { - properties.put((String) key, Convert.toByte(value)); - } else { - properties.put((String) key, value); - } - }); - if (CollUtil.isEmpty(properties)) { - log.error("[saveDeviceProperty][消息({}) 没有合法的属性]", message); - } else { - // 2.1 保存设备属性【数据】 - devicePropertyMapper.insert(device, properties, LocalDateTimeUtil.toEpochMilli(message.getReportTime())); - - // 2.2 保存设备属性【日志】 - Map properties2 = convertMap(properties.entrySet(), Map.Entry::getKey, entry -> - IotDevicePropertyDO.builder().value(entry.getValue()).updateTime(message.getReportTime()).build()); - deviceDataRedisDAO.putAll(device.getId(), properties2); - } - - // 2.3 提取 GeoLocation 并更新设备定位 - // 为什么 properties 为空,也要执行定位更新?因为可能上报的属性里,没有合法属性,但是包含 GeoLocation 定位属性 - extractAndUpdateDeviceLocation(device, (Map) message.getParams()); - } - - @Override - public Map getLatestDeviceProperties(Long deviceId) { - return deviceDataRedisDAO.get(deviceId); - } - - @Override - public List getHistoryDevicePropertyList(IotDevicePropertyHistoryListReqVO listReqVO) { - try { - return devicePropertyMapper.selectListByHistory(listReqVO); - } catch (Exception exception) { - if (exception.getMessage().contains("Table does not exist")) { - return Collections.emptyList(); - } - throw exception; - } - } - - // ========== 设备时间相关操作 ========== - - @Override - public Set getDeviceIdListByReportTime(LocalDateTime maxReportTime) { - return deviceReportTimeRedisDAO.range(maxReportTime); - } - - @Override - @Async - public void updateDeviceReportTimeAsync(Long id, LocalDateTime reportTime) { - deviceReportTimeRedisDAO.update(id, reportTime); - } - - @Override - public void updateDeviceServerIdAsync(Long id, String serverId) { - if (StrUtil.isEmpty(serverId)) { - return; - } - deviceServerIdRedisDAO.update(id, serverId); - } - - @Override - public String getDeviceServerId(Long id) { - return deviceServerIdRedisDAO.get(id); - } - - // ========== 设备定位相关操作 ========== - - /** - * 从属性中提取 GeoLocation 并更新设备定位 - * - * @see 阿里云规范 - * GeoLocation 结构体包含:Longitude, Latitude, Altitude, CoordinateSystem - */ - private void extractAndUpdateDeviceLocation(IotDeviceDO device, Map params) { - // 1. 解析 GeoLocation 经纬度坐标 - BigDecimal[] location = parseGeoLocation(params); - if (location == null) { - return; - } - - // 2. 更新设备定位 - deviceService.updateDeviceLocation(device, location[0], location[1]); - log.info("[extractAndUpdateGeoLocation][设备({}) 定位更新: lng={}, lat={}]", - device.getId(), location[0], location[1]); - } - - /** - * 从属性参数中解析 GeoLocation,返回经纬度坐标数组 [longitude, latitude] - * - * @param params 属性参数 - * @return [经度, 纬度],解析失败返回 null - */ - @SuppressWarnings("unchecked") - private BigDecimal[] parseGeoLocation(Map params) { - if (params == null) { - return null; - } - // 1. 查找 GeoLocation 属性(标识符为 GeoLocation 或 geoLocation) - Object geoValue = params.get("GeoLocation"); - if (geoValue == null) { - geoValue = params.get("geoLocation"); - } - if (geoValue == null) { - return null; - } - - // 2. 转换为 Map - Map geoLocation = null; - if (geoValue instanceof Map) { - geoLocation = (Map) geoValue; - } else if (geoValue instanceof String) { - geoLocation = JsonUtils.parseObject((String) geoValue, Map.class); - } - if (geoLocation == null) { - return null; - } - - // 3. 提取经纬度(支持阿里云命名规范:首字母大写) - BigDecimal longitude = getBigDecimal(geoLocation, "Longitude"); - if (longitude == null) { - longitude = getBigDecimal(geoLocation, "longitude"); - } - BigDecimal latitude = getBigDecimal(geoLocation, "Latitude"); - if (latitude == null) { - latitude = getBigDecimal(geoLocation, "latitude"); - } - if (longitude == null || latitude == null) { - return null; - } - // 校验经纬度范围:经度 -180 到 180,纬度 -90 到 90 - if (longitude.compareTo(BigDecimal.valueOf(-180)) < 0 || longitude.compareTo(BigDecimal.valueOf(180)) > 0 - || latitude.compareTo(BigDecimal.valueOf(-90)) < 0 || latitude.compareTo(BigDecimal.valueOf(90)) > 0) { - log.warn("[parseGeoLocation][经纬度超出有效范围: lng={}, lat={}]", longitude, latitude); - return null; - } - return new BigDecimal[]{longitude, latitude}; - } - -} \ No newline at end of file diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/deviceattributetype/DeviceAttributeTypeService.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/deviceattributetype/DeviceAttributeTypeService.java new file mode 100644 index 0000000..7db3e88 --- /dev/null +++ b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/deviceattributetype/DeviceAttributeTypeService.java @@ -0,0 +1,61 @@ +package cn.iocoder.yudao.module.iot.service.deviceattributetype; + +import cn.iocoder.yudao.framework.common.pojo.PageResult; +import cn.iocoder.yudao.module.iot.controller.admin.deviceattributetype.vo.*; +import cn.iocoder.yudao.module.iot.dal.dataobject.deviceattributetype.DeviceAttributeTypeDO; + +import javax.validation.Valid; +import java.util.List; + +/** + * 采集点分类 Service 接口 + * + * @author 内蒙必硕 + */ +public interface DeviceAttributeTypeService { + + /** + * 创建采集点分类 + * + * @param createReqVO 创建信息 + * @return 编号 + */ + Long createDeviceAttributeType(@Valid DeviceAttributeTypeSaveReqVO createReqVO); + + /** + * 更新采集点分类 + * + * @param updateReqVO 更新信息 + */ + void updateDeviceAttributeType(@Valid DeviceAttributeTypeSaveReqVO updateReqVO); + + /** + * 删除采集点分类 + * + * @param id 编号 + */ + void deleteDeviceAttributeType( List ids); + + /** + * 获得采集点分类 + * + * @param id 编号 + * @return 采集点分类 + */ + DeviceAttributeTypeDO getDeviceAttributeType(Long id); + + /** + * 获得采集点分类分页 + * + * @param pageReqVO 分页查询 + * @return 采集点分类分页 + */ + PageResult getDeviceAttributeTypePage(DeviceAttributeTypePageReqVO pageReqVO); + + /** + * 获得采集点分类列表 + * + * @return 采集点分类列表 + */ + List getDeviceAttributeTypeList(); +} \ No newline at end of file diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/deviceattributetype/DeviceAttributeTypeServiceImpl.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/deviceattributetype/DeviceAttributeTypeServiceImpl.java new file mode 100644 index 0000000..5d4283f --- /dev/null +++ b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/deviceattributetype/DeviceAttributeTypeServiceImpl.java @@ -0,0 +1,138 @@ +package cn.iocoder.yudao.module.iot.service.deviceattributetype; + +import cn.iocoder.yudao.framework.common.pojo.PageResult; +import cn.iocoder.yudao.framework.common.util.object.BeanUtils; +import cn.iocoder.yudao.module.iot.controller.admin.deviceattributetype.vo.*; +import cn.iocoder.yudao.module.iot.dal.dataobject.deviceattributetype.DeviceAttributeTypeDO; +import cn.iocoder.yudao.module.iot.dal.dataobject.devicecontactmodel.DeviceContactModelDO; +import cn.iocoder.yudao.module.iot.dal.dataobject.devicemodelattribute.DeviceModelAttributeDO; +import cn.iocoder.yudao.module.iot.dal.mysql.deviceattributetype.DeviceAttributeTypeMapper; +import cn.iocoder.yudao.module.iot.dal.mysql.devicecontactmodel.DeviceContactModelMapper; +import cn.iocoder.yudao.module.iot.dal.mysql.devicemodelattribute.DeviceModelAttributeMapper; +import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; +import com.baomidou.mybatisplus.core.toolkit.CollectionUtils; +import com.baomidou.mybatisplus.core.toolkit.Wrappers; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; +import org.springframework.validation.annotation.Validated; + +import javax.annotation.Resource; +import java.util.List; + +import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception; +import static cn.iocoder.yudao.module.iot.enums.ErrorCodeConstants.*; + +/** + * 采集点分类 Service 实现类 + * + * @author 内蒙必硕 + */ +@Service +@Validated +public class DeviceAttributeTypeServiceImpl implements DeviceAttributeTypeService { + + @Resource + private DeviceAttributeTypeMapper deviceAttributeTypeMapper; + + @Resource + private DeviceModelAttributeMapper deviceModelAttributeMapper; + + @Resource + private DeviceContactModelMapper deviceContactModelMapper; + + @Override + public Long createDeviceAttributeType(DeviceAttributeTypeSaveReqVO createReqVO) { + // 重复判断 + Long count = deviceAttributeTypeMapper.selectCount(new LambdaQueryWrapper() + .eq(DeviceAttributeTypeDO::getCode, createReqVO.getCode()) + ); + + if (count > 0) { + throw exception(DEVICE_CODE_EXISTS); + } + // 插入 + DeviceAttributeTypeDO deviceAttributeType = BeanUtils.toBean(createReqVO, DeviceAttributeTypeDO.class); + deviceAttributeTypeMapper.insert(deviceAttributeType); + // 返回 + return deviceAttributeType.getId(); + } + + @Override + public void updateDeviceAttributeType(DeviceAttributeTypeSaveReqVO updateReqVO) { + + // 重复判断 + Long count = deviceAttributeTypeMapper.selectCount(new LambdaQueryWrapper() + .eq(DeviceAttributeTypeDO::getCode, updateReqVO.getCode()) + .ne(DeviceAttributeTypeDO::getId, updateReqVO.getId()) + ); + + if (count > 0) { + throw exception(DEVICE_CODE_EXISTS); + } + + DeviceAttributeTypeDO deviceAttributeTypeDO = deviceAttributeTypeMapper.selectById(updateReqVO.getId()); + // 校验存在 + validateDeviceAttributeTypeExists(deviceAttributeTypeDO); + // 更新 + DeviceAttributeTypeDO updateObj = BeanUtils.toBean(updateReqVO, DeviceAttributeTypeDO.class); + deviceAttributeTypeMapper.updateById(updateObj); + } + + @Override + @Transactional(rollbackFor = Exception.class) + public void deleteDeviceAttributeType(List ids) { + if (CollectionUtils.isEmpty(ids)) { + return; + } + + // 批量校验存在性 + List existList = deviceAttributeTypeMapper.selectBatchIds(ids); + if (existList.size() != ids.size()) { + throw exception(DEVICE_ATTRIBUTE_TYPE_NOT_EXISTS); + } + + // 批量校验引用 + validateBatchReferences(ids); + + // 批量删除 + deviceAttributeTypeMapper.deleteByIds(ids); + } + + private void validateBatchReferences(List ids) { + // 批量查询引用 + boolean hasModelRef = deviceModelAttributeMapper.exists( + Wrappers.lambdaQuery() + .in(DeviceModelAttributeDO::getAttributeType, ids)); + + boolean hasContactRef = deviceContactModelMapper.exists( + Wrappers.lambdaQuery() + .in(DeviceContactModelDO::getAttributeType, ids)); + + if (hasModelRef || hasContactRef) { + throw exception(DEVICE_ATTRIBUTE_TYPE_REFERENCES_EXISTS); + } + } + + private void validateDeviceAttributeTypeExists(DeviceAttributeTypeDO deviceAttributeTypeDO) { + if (deviceAttributeTypeDO == null) { + throw exception(DEVICE_ATTRIBUTE_TYPE_NOT_EXISTS); + } + } + + @Override + public DeviceAttributeTypeDO getDeviceAttributeType(Long id) { + return deviceAttributeTypeMapper.selectById(id); + } + + @Override + public PageResult getDeviceAttributeTypePage(DeviceAttributeTypePageReqVO pageReqVO) { + return deviceAttributeTypeMapper.selectPage(pageReqVO); + } + + + @Override + public List getDeviceAttributeTypeList() { + return deviceAttributeTypeMapper.selectList(); + } + +} \ No newline at end of file diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/devicecontactmodel/DeviceContactModelService.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/devicecontactmodel/DeviceContactModelService.java new file mode 100644 index 0000000..e8fc5c1 --- /dev/null +++ b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/devicecontactmodel/DeviceContactModelService.java @@ -0,0 +1,58 @@ +package cn.iocoder.yudao.module.iot.service.devicecontactmodel; + +import cn.iocoder.yudao.framework.common.pojo.PageResult; +import cn.iocoder.yudao.module.iot.controller.admin.devicecontactmodel.vo.*; +import cn.iocoder.yudao.module.iot.dal.dataobject.devicecontactmodel.DeviceContactModelDO; + +import javax.validation.Valid; +import java.util.List; + +/** + * 采集设备模型-点位管理 Service 接口 + * + * @author 内蒙必硕 + */ +public interface DeviceContactModelService { + + /** + * 创建采集设备模型-点位管理 + * + * @param createReqVO 创建信息 + * @return 编号 + */ + Long createDeviceContactModel(@Valid DeviceContactModelSaveReqVO createReqVO); + + /** + * 更新采集设备模型-点位管理 + * + * @param updateReqVO 更新信息 + */ + void updateDeviceContactModel(@Valid DeviceContactModelSaveReqVO updateReqVO); + + /** + * 删除采集设备模型-点位管理 + * + * @param id 编号 + */ + void deleteDeviceContactModel(List id); + + /** + * 获得采集设备模型-点位管理 + * + * @param id 编号 + * @return 采集设备模型-点位管理 + */ + DeviceContactModelDO getDeviceContactModel(Long id); + + /** + * 获得采集设备模型-点位管理分页 + * + * @param pageReqVO 分页查询 + * @return 采集设备模型-点位管理分页 + */ + PageResult getDeviceContactModelPage(DeviceContactModelPageReqVO pageReqVO); + + List getDeviceContactModelList(Long id); + + List selectListByDeviceId(Long id); +} \ No newline at end of file diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/devicecontactmodel/DeviceContactModelServiceImpl.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/devicecontactmodel/DeviceContactModelServiceImpl.java new file mode 100644 index 0000000..5dd41b6 --- /dev/null +++ b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/devicecontactmodel/DeviceContactModelServiceImpl.java @@ -0,0 +1,153 @@ +package cn.iocoder.yudao.module.iot.service.devicecontactmodel; + +import cn.hutool.core.util.StrUtil; +import cn.iocoder.yudao.framework.common.pojo.PageResult; +import cn.iocoder.yudao.framework.common.util.object.BeanUtils; +import cn.iocoder.yudao.module.iot.controller.admin.devicecontactmodel.vo.DeviceContactModelPageReqVO; +import cn.iocoder.yudao.module.iot.controller.admin.devicecontactmodel.vo.DeviceContactModelSaveReqVO; +import cn.iocoder.yudao.module.iot.dal.dataobject.devicecontactmodel.DeviceContactModelDO; +import cn.iocoder.yudao.module.iot.dal.mysql.deviceattributetype.DeviceAttributeTypeMapper; +import cn.iocoder.yudao.module.iot.dal.mysql.devicecontactmodel.DeviceContactModelMapper; +import cn.iocoder.yudao.module.iot.service.device.TDengineService; +import com.baomidou.mybatisplus.core.toolkit.Wrappers; +import org.springframework.stereotype.Service; +import org.springframework.validation.annotation.Validated; + +import javax.annotation.Resource; +import java.time.LocalDate; +import java.time.format.DateTimeFormatter; +import java.util.List; + +import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception; +import static cn.iocoder.yudao.module.iot.enums.ErrorCodeConstants.DEVICE_CONTACT_MODEL_NOT_EXISTS; +import static cn.iocoder.yudao.module.iot.enums.ErrorCodeConstants.DEVICE_MODEL_POINT_CODE_EXISTS; + +/** + * 采集设备模型-点位管理 Service 实现类 + * + * @author 内蒙必硕 + */ +@Service +@Validated +public class DeviceContactModelServiceImpl implements DeviceContactModelService { + + @Resource + private DeviceContactModelMapper deviceContactModelMapper; + @Resource + private DeviceAttributeTypeMapper deviceAttributeTypeMapper; + @Resource + private TDengineService tDengineService; + + @Override + public Long createDeviceContactModel(DeviceContactModelSaveReqVO createReqVO) { + + + //判断编码是否唯一 + boolean exists = deviceContactModelMapper.exists( + Wrappers.lambdaQuery() + .eq(DeviceContactModelDO::getDeviceId, createReqVO.getDeviceId()) + .eq(DeviceContactModelDO::getAttributeCode, createReqVO.getAttributeCode())); + + if (exists) { + throw exception(DEVICE_MODEL_POINT_CODE_EXISTS); + } + + // 2. 检查attributeCode是否符合TDengine列名规则 + if (StrUtil.isNotBlank(createReqVO.getAttributeCode())) { + tDengineService.validateColumnName(createReqVO.getAttributeCode()); + } + + // 插入 + DeviceContactModelDO deviceContactModel = BeanUtils.toBean(createReqVO, DeviceContactModelDO.class); +// deviceContactModel.setTypeName(deviceAttributeTypeMapper.selectById(createReqVO.getAttributeCode()).getName()); + deviceContactModelMapper.insert(deviceContactModel); + + //新增td数据库列 + tDengineService.AddTDDatabaseColumn(createReqVO.getDeviceId(),deviceContactModel); + + // 返回 + return deviceContactModel.getId(); + } + + @Override + public void updateDeviceContactModel(DeviceContactModelSaveReqVO updateReqVO) { + // 校验存在 + validateDeviceContactModelExists(updateReqVO.getId()); + // 更新 + DeviceContactModelDO updateObj = BeanUtils.toBean(updateReqVO, DeviceContactModelDO.class); + deviceContactModelMapper.updateById(updateObj); + } + + @Override + public void deleteDeviceContactModel(List ids) { + for (Long id : ids) { + // 校验存在 + validateDeviceContactModelExists(id); + + DeviceContactModelDO deviceContactModelDO = deviceContactModelMapper.selectById(id); + if (deviceContactModelDO == null) { + continue; + } + + Long deviceId = deviceContactModelDO.getDeviceId(); + String oldColumnName = deviceContactModelDO.getAttributeCode(); + + if (deviceId != null && StrUtil.isNotBlank(oldColumnName)) { + // 生成带序号的历史列名 + String newColumnName = generateHistoryColumnName(deviceId, oldColumnName); + + // 重命名列 + tDengineService.renameTDColumn(deviceId, oldColumnName, newColumnName); + } + + // 删除 + deviceContactModelMapper.deleteById(id); + } + } + + /** + * 生成带序号的历史列名 + * 格式: DEL_{原列名}_{日期}_{序号} + * 示例: DEL_temperature_20260327_1 + */ + private String generateHistoryColumnName(Long deviceId, String originalName) { + String date = LocalDate.now().format(DateTimeFormatter.ofPattern("yyyyMMdd")); + + // 获取当前序号 + int sequence = tDengineService.calculateSequence(deviceId, originalName, date); + + return String.format("DEL_%s_%s_%d", originalName, date, sequence); + } + + + + + private void validateDeviceContactModelExists(Long id) { + if (deviceContactModelMapper.selectById(id) == null) { + throw exception(DEVICE_CONTACT_MODEL_NOT_EXISTS); + } + } + + @Override + public DeviceContactModelDO getDeviceContactModel(Long id) { + DeviceContactModelDO deviceContactModelDO = deviceContactModelMapper.selectById(id); + return deviceContactModelDO; + } + + @Override + public PageResult getDeviceContactModelPage(DeviceContactModelPageReqVO pageReqVO) { + return deviceContactModelMapper.selectPage(pageReqVO); + } + + @Override + public List getDeviceContactModelList(Long id) { + return deviceContactModelMapper.selectList(Wrappers.lambdaQuery().eq(DeviceContactModelDO::getDeviceId,id) + .orderByDesc(DeviceContactModelDO::getCreateTime)); + } + + @Override + public List selectListByDeviceId(Long id) { + return deviceContactModelMapper.selectList(Wrappers.lambdaQuery().eq(DeviceContactModelDO::getDeviceId,id)); + } + +} \ No newline at end of file diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/devicemodel/DeviceModelService.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/devicemodel/DeviceModelService.java new file mode 100644 index 0000000..82d3e1a --- /dev/null +++ b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/devicemodel/DeviceModelService.java @@ -0,0 +1,68 @@ +package cn.iocoder.yudao.module.iot.service.devicemodel; + +import cn.iocoder.yudao.framework.common.pojo.PageResult; +import cn.iocoder.yudao.module.iot.controller.admin.devicemodel.vo.DeviceModelPageReqVO; +import cn.iocoder.yudao.module.iot.controller.admin.devicemodel.vo.DeviceModelSaveReqVO; +import cn.iocoder.yudao.module.iot.dal.dataobject.devicemodel.DeviceModelDO; + +import javax.validation.Valid; +import java.util.List; + +/** + * 采集设备模型 Service 接口 + * + * @author 内蒙必硕 + */ +public interface DeviceModelService { + + /** + * 创建采集设备模型 + * + * @param createReqVO 创建信息 + * @return 编号 + */ + Long createDeviceModel(@Valid DeviceModelSaveReqVO createReqVO); + + /** + * 更新采集设备模型 + * + * @param updateReqVO 更新信息 + */ + void updateDeviceModel(@Valid DeviceModelSaveReqVO updateReqVO); + + /** + * 删除采集设备模型 + * + * @param id 编号 + */ + void deleteDeviceModel(List idList); + + /** + * 获得采集设备模型 + * + * @param id 编号 + * @return 采集设备模型 + */ + DeviceModelDO getDeviceModel(Long id); + + /** + * 获得采集设备模型分页 + * + * @param pageReqVO 分页查询 + * @return 采集设备模型分页 + */ + PageResult getDeviceModelPage(DeviceModelPageReqVO pageReqVO); + /** + * 获得采集设备模型列表 + * + * @return 采集设备模型列表 + */ + List getDeviceModelList(); + + /** + * 复制设备模型 + * + * @return 复制设备模型 + */ + Long copyDevice(Long id); +} \ No newline at end of file diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/devicemodel/DeviceModelServiceImpl.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/devicemodel/DeviceModelServiceImpl.java new file mode 100644 index 0000000..8226255 --- /dev/null +++ b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/devicemodel/DeviceModelServiceImpl.java @@ -0,0 +1,191 @@ +package cn.iocoder.yudao.module.iot.service.devicemodel; + +import cn.iocoder.yudao.framework.common.pojo.PageResult; +import cn.iocoder.yudao.framework.common.util.object.BeanUtils; +import cn.iocoder.yudao.module.iot.controller.admin.devicemodel.vo.DeviceModelPageReqVO; +import cn.iocoder.yudao.module.iot.controller.admin.devicemodel.vo.DeviceModelSaveReqVO; +import cn.iocoder.yudao.module.iot.dal.dataobject.devicemodel.DeviceModelDO; +import cn.iocoder.yudao.module.iot.dal.dataobject.devicemodelattribute.DeviceModelAttributeDO; +import cn.iocoder.yudao.module.iot.dal.dataobject.devicemodelrules.DeviceModelRulesDO; +import cn.iocoder.yudao.module.iot.dal.mysql.device.DeviceMapper; +import cn.iocoder.yudao.module.iot.dal.mysql.devicemodel.DeviceModelMapper; +import cn.iocoder.yudao.module.iot.dal.mysql.devicemodelattribute.DeviceModelAttributeMapper; +import cn.iocoder.yudao.module.iot.dal.mysql.devicemodelrules.DeviceModelRulesMapper; +import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; +import com.baomidou.mybatisplus.core.toolkit.Wrappers; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; +import org.springframework.validation.annotation.Validated; + +import javax.annotation.Resource; +import java.util.ArrayList; +import java.util.List; +import java.util.Random; + +import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception; +import static cn.iocoder.yudao.module.iot.enums.ErrorCodeConstants.DEVICE_MODEL_NOT_EXISTS; + +/** + * 采集设备模型 Service 实现类 + * + * @author 内蒙必硕 + */ +@Service +@Validated +public class DeviceModelServiceImpl implements DeviceModelService { + + @Resource + private DeviceModelMapper deviceModelMapper; + + @Resource + private DeviceMapper deviceMapper; + + @Resource + private DeviceModelAttributeMapper deviceModelAttributeMapper; + + @Resource + private DeviceModelRulesMapper deviceModelRulesMapper; + + @Override + public Long createDeviceModel(DeviceModelSaveReqVO createReqVO) { + //编码重复判断 + Long count = deviceModelMapper.selectCount(new LambdaQueryWrapper() + .eq(DeviceModelDO::getCode, createReqVO.getCode()) + ); + + if (count > 0) { + throw exception(DEVICE_MODEL_CODE_EXISTS); + } + + // 插入 + DeviceModelDO deviceModel = BeanUtils.toBean(createReqVO, DeviceModelDO.class); + deviceModelMapper.insert(deviceModel); + + //插入点位规则 + insertionPointRule(deviceModel.getId()); + + + // 返回 + return deviceModel.getId(); + } + + private void insertionPointRule(Long id) { + //TODO 待优化RUNNING及ALRAM + List deviceModelRulesDOList = new ArrayList<>(); + + DeviceModelRulesDO deviceModelRulesDO = new DeviceModelRulesDO(); + deviceModelRulesDO.setIdentifier("RUNNING"); + deviceModelRulesDO.setFieldName("运行"); + deviceModelRulesDO.setDefaultValue("运行"); + deviceModelRulesDO.setModelId(id); + deviceModelRulesDOList.add(deviceModelRulesDO); +// +// DeviceModelRulesDO deviceModelRulesDO1 = new DeviceModelRulesDO(); +// deviceModelRulesDO1.setIdentifier("ALARM"); +// deviceModelRulesDO1.setFieldName("报警"); +// deviceModelRulesDO1.setDefaultValue("报警"); +// deviceModelRulesDO1.setModelId(id); +// deviceModelRulesDOList.add(deviceModelRulesDO1); + + deviceModelRulesMapper.insertBatch(deviceModelRulesDOList); + + } + + @Override + public void updateDeviceModel(DeviceModelSaveReqVO updateReqVO) { + //编码重复判断 + Long count = deviceModelMapper.selectCount(new LambdaQueryWrapper() + .eq(DeviceModelDO::getCode, updateReqVO.getCode()) + .ne(DeviceModelDO::getId, updateReqVO.getId()) + + ); + + if (count > 0) { + throw exception(DEVICE_MODEL_CODE_EXISTS); + } + // 校验存在 + validateDeviceModelExists(updateReqVO.getId()); + // 更新 + DeviceModelDO updateObj = BeanUtils.toBean(updateReqVO, DeviceModelDO.class); + deviceModelMapper.updateById(updateObj); + } + + @Override + @Transactional(rollbackFor = Exception.class) + public void deleteDeviceModel(List idList ) { + + for (Long id : idList) { + // 校验存在 + validateDeviceModelExists(id); + //删除附属子表 + deleteModelAttribute(id); + } + // 删除 + deviceModelMapper.deleteByIds(idList); + } + + private void deleteModelAttribute(Long id) { + LambdaQueryWrapper lambdaQueryWrapper = new LambdaQueryWrapper<>(); + lambdaQueryWrapper.eq(DeviceModelAttributeDO::getDeviceModelId,id); + deviceModelAttributeMapper.delete(lambdaQueryWrapper); + } + + private void validateDeviceModelExists(Long id) { + if (deviceModelMapper.selectById(id) == null) { + throw exception(DEVICE_MODEL_NOT_EXISTS); + } + } + + @Override + public DeviceModelDO getDeviceModel(Long id) { + return deviceModelMapper.selectById(id); + } + + @Override + public PageResult getDeviceModelPage(DeviceModelPageReqVO pageReqVO) { + return deviceModelMapper.selectPage(pageReqVO); + } + + @Override + public List getDeviceModelList() { + return deviceModelMapper.selectList(Wrappers.lambdaQuery().orderByDesc(DeviceModelDO::getCreateTime)); + } + + @Override + @Transactional(rollbackFor = Exception.class) + public Long copyDevice(Long id) { + if (id == null){ + throw new RuntimeException("采集设备模型Id不能为空"); + } + DeviceModelDO deviceModelDO = deviceModelMapper.selectById(id); + if(deviceModelDO == null){ + throw new RuntimeException("该采集设备模型不存在!"); + } + //复制实体类 + DeviceModelDO newDeviceMode = new DeviceModelDO(); + BeanUtils.copyProperties(deviceModelDO, newDeviceMode); + newDeviceMode.setId(null); + Random random = new Random(); + int randomNumber = random.nextInt(9000) + 1000; + newDeviceMode.setCode(deviceModelDO.getCode()+ "-" + randomNumber); + newDeviceMode.setName(deviceModelDO.getName() + "-副本"); + deviceModelMapper.insert(newDeviceMode); + + //复制关联表 + LambdaQueryWrapper lambdaConversionException = new LambdaQueryWrapper(); + lambdaConversionException.eq(DeviceModelAttributeDO::getDeviceModelId,deviceModelDO.getId()); + List deviceModelAttributeDOS = deviceModelAttributeMapper.selectList(lambdaConversionException); + + + if (deviceModelAttributeDOS != null && !deviceModelAttributeDOS.isEmpty()){ + for (DeviceModelAttributeDO deviceModelAttributeDO : deviceModelAttributeDOS) { + deviceModelAttributeDO.setId(null); + deviceModelAttributeDO.setDeviceModelId(newDeviceMode.getId()); + } + deviceModelAttributeMapper.insertBatch(deviceModelAttributeDOS); + } + + return newDeviceMode.getId(); + } + +} \ No newline at end of file diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/devicemodelattribute/DeviceModelAttributeService.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/devicemodelattribute/DeviceModelAttributeService.java new file mode 100644 index 0000000..dcec0a1 --- /dev/null +++ b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/devicemodelattribute/DeviceModelAttributeService.java @@ -0,0 +1,67 @@ +package cn.iocoder.yudao.module.iot.service.devicemodelattribute; + +import cn.iocoder.yudao.framework.common.pojo.PageResult; +import cn.iocoder.yudao.module.iot.controller.admin.devicemodelattribute.vo.*; +import cn.iocoder.yudao.module.iot.dal.dataobject.devicemodelattribute.DeviceModelAttributeDO; + +import javax.validation.Valid; +import java.util.List; +import java.util.Map; + +/** + * 采集设备模型-点位管理 Service 接口 + * + * @author 内蒙必硕 + */ +public interface DeviceModelAttributeService { + + /** + * 创建采集设备模型-点位管理 + * + * @param createReqVO 创建信息 + * @return 编号 + */ + Long createDeviceModelAttribute(@Valid DeviceModelAttributeSaveReqVO createReqVO); + + /** + * 更新采集设备模型-点位管理 + * + * @param updateReqVO 更新信息 + */ + void updateDeviceModelAttribute(@Valid DeviceModelAttributeSaveReqVO updateReqVO); + + /** + * 删除采集设备模型-点位管理 + * + * @param idList + */ + void deleteDeviceModelAttribute(List idList ); + + /** + * 获得采集设备模型-点位管理 + * + * @param id 编号 + * @return 采集设备模型-点位管理 + */ + DeviceModelAttributeDO getDeviceModelAttribute(Long id); + + /** + * 获得采集设备模型-点位管理分页 + * + * @param pageReqVO 分页查询 + * @return 采集设备模型-点位管理分页 + */ + PageResult getDeviceModelAttributePage(DeviceModelAttributePageReqVO pageReqVO); + + List> operationAnalysisDetails(Long deviceId, Long modelId,String collectionStartTime, String collectionEndTime); + + List getDeviceModelAttributeList(Long id); + /** + * 导入设备采集点位列表 + * 【对应 UserService 的 importUserList 方法】 + */ + DeviceModelAttributeImportRespVO importAttributeList(List importAttributes, Long deviceModelId, boolean isUpdateSupport); + + + +} \ No newline at end of file diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/devicemodelattribute/DeviceModelAttributeServiceImpl.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/devicemodelattribute/DeviceModelAttributeServiceImpl.java new file mode 100644 index 0000000..cb34b57 --- /dev/null +++ b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/devicemodelattribute/DeviceModelAttributeServiceImpl.java @@ -0,0 +1,413 @@ +package cn.iocoder.yudao.module.iot.service.devicemodelattribute; + +import cn.hutool.core.collection.CollUtil; +import cn.iocoder.yudao.framework.common.exception.ServiceException; +import cn.iocoder.yudao.framework.common.pojo.PageResult; +import cn.iocoder.yudao.framework.common.util.collection.MapUtils; +import cn.iocoder.yudao.framework.common.util.object.BeanUtils; +import cn.iocoder.yudao.framework.common.util.validation.ValidationUtils; + +import cn.iocoder.yudao.module.iot.controller.admin.device.enums.JavaToTdengineTypeEnum; +import cn.iocoder.yudao.module.iot.controller.admin.devicemodelattribute.vo.*; +import cn.iocoder.yudao.module.iot.dal.dataobject.deviceattributetype.DeviceAttributeTypeDO; +import cn.iocoder.yudao.module.iot.dal.dataobject.devicecontactmodel.DeviceContactModelDO; +import cn.iocoder.yudao.module.iot.dal.dataobject.devicemodelattribute.DeviceModelAttributeDO; +import cn.iocoder.yudao.module.iot.dal.dataobject.product.ErpProductUnitDO; +import cn.iocoder.yudao.module.iot.dal.mysql.deviceattributetype.DeviceAttributeTypeMapper; +import cn.iocoder.yudao.module.iot.dal.mysql.devicecontactmodel.DeviceContactModelMapper; +import cn.iocoder.yudao.module.iot.dal.mysql.devicemodelattribute.DeviceModelAttributeMapper; +import cn.iocoder.yudao.module.iot.enums.ErrorCodeConstants; +import cn.iocoder.yudao.module.iot.service.device.TDengineService; +import cn.iocoder.yudao.module.iot.service.product.ErpProductUnitService; +import com.baomidou.mybatisplus.core.toolkit.Wrappers; +import lombok.extern.slf4j.Slf4j; +import org.apache.commons.lang3.StringUtils; +import org.springframework.context.annotation.Lazy; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; +import org.springframework.validation.annotation.Validated; + +import javax.annotation.Resource; +import javax.validation.ConstraintViolationException; +import java.sql.Timestamp; +import java.text.SimpleDateFormat; +import java.util.*; +import java.util.stream.Collectors; + +import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception; +import static cn.iocoder.yudao.module.iot.enums.ErrorCodeConstants.*; + +/** + * 采集设备模型-点位管理 Service 实现类 + * + * @author 内蒙必硕 + */ +@Service +@Validated +@Slf4j + public class DeviceModelAttributeServiceImpl implements DeviceModelAttributeService { + + @Resource + private DeviceModelAttributeMapper deviceModelAttributeMapper; + + @Resource + private TDengineService tdengineService; + + @Resource + private DeviceAttributeTypeMapper deviceAttributeTypeMapper; + + @Resource + @Lazy + private DeviceContactModelMapper deviceContactModelMapper; + + @Resource + private ErpProductUnitService productUnitService; + + + @Override + public Long createDeviceModelAttribute(DeviceModelAttributeSaveReqVO createReqVO) { + + //判断编码是否唯一 + boolean exists = deviceModelAttributeMapper.exists( + Wrappers.lambdaQuery() + .eq(DeviceModelAttributeDO::getDeviceModelId, createReqVO.getDeviceModelId()) + .eq(DeviceModelAttributeDO::getAttributeCode, createReqVO.getAttributeCode())); + + if (exists) { + throw exception(DEVICE_MODEL_ATTRIBUTE_POTIN_CODE_EXISTS); + } + + // 插入 + DeviceModelAttributeDO deviceModelAttribute = BeanUtils.toBean(createReqVO, DeviceModelAttributeDO.class); +// deviceModelAttribute.setTypeName(deviceAttributeTypeMapper.selectById(createReqVO.getAttributeCode()).getName()); + deviceModelAttributeMapper.insert(deviceModelAttribute); + // 返回 + return deviceModelAttribute.getId(); + } + + @Override + public void updateDeviceModelAttribute(DeviceModelAttributeSaveReqVO updateReqVO) { + // 校验存在 + validateDeviceModelAttributeExists(updateReqVO.getId()); + // 更新 + DeviceModelAttributeDO updateObj = BeanUtils.toBean(updateReqVO, DeviceModelAttributeDO.class); + deviceModelAttributeMapper.updateById(updateObj); + } + + @Override + @Transactional(rollbackFor = Exception.class) + public void deleteDeviceModelAttribute(List idList) { + for (Long id : idList) { + // 校验存在 + validateDeviceModelAttributeExists(id); + } + // 删除 + deviceModelAttributeMapper.deleteByIds(idList); + } + + private void validateDeviceModelAttributeExists(Long id) { + if (deviceModelAttributeMapper.selectById(id) == null) { + throw exception(DEVICE_MODEL_ATTRIBUTE_NOT_EXISTS); + } + } + + @Override + public DeviceModelAttributeDO getDeviceModelAttribute(Long id) { + return deviceModelAttributeMapper.selectById(id); + } + + @Override + public PageResult getDeviceModelAttributePage(DeviceModelAttributePageReqVO pageReqVO) { + // 获取设备模型属性分页数据 + PageResult pageResult = deviceModelAttributeMapper.selectPage(pageReqVO); + + +// // 获取所有属性类型并构建映射 +// List attributeTypes = deviceAttributeTypeMapper.selectList(); +// Map typeNameMap = attributeTypes.stream() +// .collect(Collectors.toMap(DeviceAttributeTypeDO::getId, DeviceAttributeTypeDO::getName)); + Map unitMap = productUnitService.getProductUnitMap( + pageResult.getList().stream() + .map(DeviceModelAttributeDO::getDataUnit) + .filter(StringUtils::isNotEmpty) + .map(str -> { + try { + return Long.valueOf(str.trim()); + } catch (NumberFormatException e) { + return null; + } + }) + .filter(Objects::nonNull) + .collect(Collectors.toSet()) + ); + // 转换并设置类型名称 + PageResult respPageResult = BeanUtils.toBean(pageResult, DeviceModelAttributeRespVO.class, deviceModelAttribute -> { + String dataUnitStr = deviceModelAttribute.getDataUnit(); + if (StringUtils.isNotBlank(dataUnitStr)) { + try { + Long dataUnitId = Long.valueOf(dataUnitStr.trim()); + MapUtils.findAndThen(unitMap, dataUnitId, + unit -> deviceModelAttribute.setDataUnitName(unit.getName())); + } catch (NumberFormatException e) { + + } + } + }); + + List list = respPageResult.getList(); + if (CollUtil.isEmpty(list)) { + return respPageResult; + } + + // 收集 attributeType + Set typeIds = list.stream() + .map(DeviceModelAttributeRespVO::getAttributeType) + .filter(Objects::nonNull) + .collect(Collectors.toSet()); + + if (CollUtil.isEmpty(typeIds)) { + return respPageResult; + } + + // 查询并转成 id -> code + Map typeCodeMap = deviceAttributeTypeMapper.selectBatchIds(typeIds) + .stream() + .collect(Collectors.toMap(DeviceAttributeTypeDO::getId, DeviceAttributeTypeDO::getCode)); + + // 赋值 + list.forEach(item -> + item.setAttributeTypeCode(typeCodeMap.get(Long.valueOf(item.getAttributeType()))) + ); +// respPageResult.getList().forEach(item -> { +// String typeName = typeNameMap.get(item.getAttributeType()); +// if (typeName != null) { +// item.setTypeName(typeName); +// } +// }); + + return respPageResult; + } + + @Override + public List> operationAnalysisDetails(Long deviceId, Long modelId, + String collectionStartTime, String collectionEndTime) { + + if (deviceId == null) { + throw exception(DEVICE_ID_DOES_NOT_EXIST); + } + if (modelId == null) { + throw exception(POINT_ID_MODEL_NOT_EXISTS); + } + + List> resultList = new ArrayList<>(); + + try { + // 获取最新10条设备数据 + List> deviceDataList = + tdengineService.newSelectLatestData(deviceId, collectionStartTime, collectionEndTime, null); + + // 查找 modelId 对应的 attributeCode + DeviceContactModelDO modelDO = deviceContactModelMapper.selectById(modelId); + if (modelDO == null) { + throw exception(POINT_ID_MODEL_NOT_EXISTS); + } + + String attributeCode = modelDO.getAttributeCode(); + if (StringUtils.isBlank(attributeCode)) { + return resultList; + } + + // 遍历时间点数据 + for (Map deviceData : deviceDataList) { + + Timestamp timestamp = (Timestamp) deviceData.get("ts"); + if (timestamp == null) continue; + + SimpleDateFormat sdf = new SimpleDateFormat("yy-MM-dd HH:mm:ss"); + + Map timePointData = new LinkedHashMap<>(); + timePointData.put("collectTime", sdf.format(timestamp)); + + // modelId 对应的字段 + Object value = deviceData.get(attributeCode); + + timePointData.put("attributeName", attributeCode); + timePointData.put("addressValue", value); + + resultList.add(timePointData); + } + + } catch (Exception e) { + log.error("处理设备 {} 数据时发生异常", deviceId, e); + return new ArrayList<>(); + } + + return resultList; + } + @Override + public List getDeviceModelAttributeList(Long id) { + + return deviceModelAttributeMapper.selectList(Wrappers.lambdaQuery().eq(DeviceModelAttributeDO::getDeviceModelId,id)); + } + + + @Override +// @Transactional(rollbackFor = Exception.class) // 完全复用你的事务注解,异常回滚所有导入 + public DeviceModelAttributeImportRespVO importAttributeList(List importAttributes, Long deviceModelId, boolean isUpdateSupport) { + // 1.1 参数校验 - 完全复刻 User 的空列表校验逻辑 + if (CollUtil.isEmpty(importAttributes)) { + throw exception(DEVICE_MODEL_ATTRIBUTE_NOT_EXISTS); + } + // 1.2 校验设备型号ID是否存在 + if (deviceModelId == null) { + throw exception(DEVICE_MODEL_NOT_EXISTS); + } + + // 2. 构建点位类型名称到ID的映射 + List attributeTypes = deviceAttributeTypeMapper.selectList(); + Map typeCodeToIdMap = attributeTypes.stream() + .collect(Collectors.toMap(DeviceAttributeTypeDO::getCode, type -> String.valueOf(type.getId()))); + + Map typeCodeToNameMap = attributeTypes.stream() + .collect(Collectors.toMap(DeviceAttributeTypeDO::getCode, DeviceAttributeTypeDO::getName)); + + + + // 3. 初始化返回 VO - 完全复刻 User 的 builder 方式,使用 LinkedHashMap 保持顺序 + DeviceModelAttributeImportRespVO respVO = DeviceModelAttributeImportRespVO.builder() + .createCodes(new ArrayList<>()) + .updateCodes(new ArrayList<>()) + .failureCodes(new LinkedHashMap<>()) + .build(); + + // 4. 遍历,逐个创建 or 更新 - 完全复刻 User 的 forEach 逻辑 + importAttributes.forEach(importAttribute -> { + // 4.1 设置设备型号ID + importAttribute.setDeviceModelId(deviceModelId); + + // 4.2 处理点位类型转换 + String code = importAttribute.getTypeCode(); + + // 不能为空 + if (StringUtils.isBlank(code)) { + respVO.getFailureCodes().put(importAttribute.getAttributeCode(), "点位类型编码不能为空"); + return; + } + // 必须存在于数据库类型列表中(防止乱填) + if (!typeCodeToIdMap.containsKey(code)) { + respVO.getFailureCodes().put(importAttribute.getAttributeCode(), + "点位类型名称不存在: " + code); + return; + } + String attributeTypeId = typeCodeToIdMap.get(code); + // 设置点位类型ID + importAttribute.setAttributeType(attributeTypeId); + + //dataType校验 + String dataType = importAttribute.getDataType(); + + if (StringUtils.isBlank(dataType)) { + respVO.getFailureCodes().put(importAttribute.getAttributeCode(), + "数据类型 不能为空"); + return; + } + dataType = StringUtils.trim(dataType); + + // 必须在枚举中存在 + JavaToTdengineTypeEnum typeEnum = JavaToTdengineTypeEnum.fromJavaType(dataType); + if (typeEnum == null) { + respVO.getFailureCodes().put(importAttribute.getAttributeCode(), + "数据类型 非法,仅支持: byte, short, int, long, float, double, boolean, char"); + return; + } + + // 4.3.1 校验字段是否符合要求 - 完全复刻 User 的 ValidationUtils.validate 调用 + try { + // 注意:如果有 DeviceModelAttributeSaveReqVO,替换为该 VO;无则直接校验 importAttribute + ValidationUtils.validate(BeanUtils.toBean(importAttribute, DeviceModelAttributeSaveReqVO.class)); + } catch (ConstraintViolationException ex) { + respVO.getFailureCodes().put(importAttribute.getAttributeCode(), ex.getMessage()); + return; + } + + // 4.3.2 业务规则校验 - 完全复刻 User 的 validateUserForCreateOrUpdate 逻辑 + try { + validateAttributeForCreateOrUpdate(deviceModelId, importAttribute.getAttributeCode()); + } catch (ServiceException ex) { + respVO.getFailureCodes().put(importAttribute.getAttributeCode(), ex.getMessage()); + return; + } + + // 4.4 查询数据库是否已存在 + DeviceModelAttributeDO existAttribute = + deviceModelAttributeMapper.selectOne( + Wrappers.lambdaQuery() + .eq(DeviceModelAttributeDO::getDeviceModelId, deviceModelId) + .eq(DeviceModelAttributeDO::getAttributeCode, importAttribute.getAttributeCode()) + ); + + // ===== 不允许覆盖 ===== + if (!isUpdateSupport) { + + // 如果已存在 → 直接失败 + if (existAttribute != null) { + respVO.getFailureCodes().put(importAttribute.getAttributeCode(), + "点位编码 已存在,不允许重复"); + return; + } + + // 不存在 → 新增 + DeviceModelAttributeDO deviceModelAttribute = + BeanUtils.toBean(importAttribute, DeviceModelAttributeDO.class); + deviceModelAttribute.setTypeName(typeCodeToNameMap.get(code)); + + deviceModelAttributeMapper.insert(deviceModelAttribute); + respVO.getCreateCodes().add(importAttribute.getAttributeCode()); + return; + } + + // ===== 允许覆盖 ===== + + // 不存在 → 新增 + if (existAttribute == null) { + DeviceModelAttributeDO deviceModelAttribute = + BeanUtils.toBean(importAttribute, DeviceModelAttributeDO.class); + deviceModelAttribute.setTypeName(typeCodeToNameMap.get(code)); + + deviceModelAttributeMapper.insert(deviceModelAttribute); + respVO.getCreateCodes().add(importAttribute.getAttributeCode()); + return; + } + + // 已存在 → 更新 + DeviceModelAttributeDO updateAttribute = + BeanUtils.toBean(importAttribute, DeviceModelAttributeDO.class); + updateAttribute.setId(existAttribute.getId()); + updateAttribute.setTypeName(typeCodeToNameMap.get(code)); + + deviceModelAttributeMapper.updateById(updateAttribute); + respVO.getUpdateCodes().add(importAttribute.getAttributeCode()); + }); + + return respVO; + } + + /** + * 校验设备采集点位创建/更新的业务规则 + * 【对应 User 的 validateUserForCreateOrUpdate 方法】 + */ + private void validateAttributeForCreateOrUpdate(Long deviceModelId, String code) { + // 1. 校验设备型号是否存在(可根据你的业务补充,对应 User 的部门校验) + if (deviceModelId == null) { + throw exception(ErrorCodeConstants.DEVICE_MODEL_NOT_EXISTS); // 替换为你项目中的设备型号不存在错误码 + } + // 2. 可补充其他业务校验(如数据类型合法性、单位长度等) + } + + + + + + + +} \ No newline at end of file diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/devicemodelrules/DeviceModelRulesService.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/devicemodelrules/DeviceModelRulesService.java new file mode 100644 index 0000000..fddfa2b --- /dev/null +++ b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/devicemodelrules/DeviceModelRulesService.java @@ -0,0 +1,55 @@ +package cn.iocoder.yudao.module.iot.service.devicemodelrules; + +import cn.iocoder.yudao.framework.common.pojo.PageResult; +import cn.iocoder.yudao.module.iot.controller.admin.devicemodelrules.vo.DeviceModelRulesPageReqVO; +import cn.iocoder.yudao.module.iot.controller.admin.devicemodelrules.vo.DeviceModelRulesSaveReqVO; +import cn.iocoder.yudao.module.iot.dal.dataobject.devicemodelrules.DeviceModelRulesDO; + +import javax.validation.Valid; + +/** + * 点位规则关联模型 Service 接口 + * + * @author 必硕科技 + */ +public interface DeviceModelRulesService { + + /** + * 创建点位规则关联模型 + * + * @param createReqVO 创建信息 + * @return 编号 + */ + Long createDeviceModelRules(@Valid DeviceModelRulesSaveReqVO createReqVO); + + /** + * 更新点位规则关联模型 + * + * @param updateReqVO 更新信息 + */ + void updateDeviceModelRules(@Valid DeviceModelRulesSaveReqVO updateReqVO); + + /** + * 删除点位规则关联模型 + * + * @param id 编号 + */ + void deleteDeviceModelRules(Long id); + + /** + * 获得点位规则关联模型 + * + * @param id 编号 + * @return 点位规则关联模型 + */ + DeviceModelRulesDO getDeviceModelRules(Long id); + + /** + * 获得点位规则关联模型分页 + * + * @param pageReqVO 分页查询 + * @return 点位规则关联模型分页 + */ + PageResult getDeviceModelRulesPage(DeviceModelRulesPageReqVO pageReqVO); + +} \ No newline at end of file diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/devicemodelrules/DeviceModelRulesServiceImpl.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/devicemodelrules/DeviceModelRulesServiceImpl.java new file mode 100644 index 0000000..9bd35a6 --- /dev/null +++ b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/devicemodelrules/DeviceModelRulesServiceImpl.java @@ -0,0 +1,78 @@ +package cn.iocoder.yudao.module.iot.service.devicemodelrules; + +import cn.iocoder.yudao.framework.common.pojo.PageResult; +import cn.iocoder.yudao.framework.common.util.object.BeanUtils; +import cn.iocoder.yudao.module.iot.controller.admin.devicemodelrules.vo.DeviceModelRulesPageReqVO; +import cn.iocoder.yudao.module.iot.controller.admin.devicemodelrules.vo.DeviceModelRulesSaveReqVO; +import cn.iocoder.yudao.module.iot.dal.dataobject.devicemodelrules.DeviceModelRulesDO; +import cn.iocoder.yudao.module.iot.dal.mysql.devicemodelrules.DeviceModelRulesMapper; +import com.alibaba.fastjson.JSON; +import org.springframework.stereotype.Service; +import org.springframework.validation.annotation.Validated; + +import javax.annotation.Resource; + +import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception; +import static cn.iocoder.yudao.module.iot.enums.ErrorCodeConstants.DEVICE_MODEL_RULES_NOT_EXISTS; + +/** + * 点位规则关联模型 Service 实现类 + * + * @author 必硕科技 + */ +@Service +@Validated +public class DeviceModelRulesServiceImpl implements DeviceModelRulesService { + + @Resource + private DeviceModelRulesMapper deviceModelRulesMapper; + + @Override + public Long createDeviceModelRules(DeviceModelRulesSaveReqVO createReqVO) { + // 插入 + DeviceModelRulesDO deviceModelRules = BeanUtils.toBean(createReqVO, DeviceModelRulesDO.class); + deviceModelRulesMapper.insert(deviceModelRules); + // 返回 + return deviceModelRules.getId(); + } + + @Override + public void updateDeviceModelRules(DeviceModelRulesSaveReqVO updateReqVO) { + // 校验存在 + validateDeviceModelRulesExists(updateReqVO.getId()); + // 更新 + DeviceModelRulesDO updateObj = BeanUtils.toBean(updateReqVO, DeviceModelRulesDO.class); + if (!updateReqVO.getPointRulesVOList().isEmpty()){ + String jsonString = JSON.toJSONString(updateReqVO.getPointRulesVOList()); + updateObj.setFieldRule(jsonString); + + } + + deviceModelRulesMapper.updateById(updateObj); + } + + @Override + public void deleteDeviceModelRules(Long id) { + // 校验存在 + validateDeviceModelRulesExists(id); + // 删除 + deviceModelRulesMapper.deleteById(id); + } + + private void validateDeviceModelRulesExists(Long id) { + if (deviceModelRulesMapper.selectById(id) == null) { + throw exception(DEVICE_MODEL_RULES_NOT_EXISTS); + } + } + + @Override + public DeviceModelRulesDO getDeviceModelRules(Long id) { + return deviceModelRulesMapper.selectById(id); + } + + @Override + public PageResult getDeviceModelRulesPage(DeviceModelRulesPageReqVO pageReqVO) { + return deviceModelRulesMapper.selectPage(pageReqVO); + } + +} \ No newline at end of file diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/deviceoperationrecord/DeviceOperationRecordService.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/deviceoperationrecord/DeviceOperationRecordService.java new file mode 100644 index 0000000..4c0e3a8 --- /dev/null +++ b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/deviceoperationrecord/DeviceOperationRecordService.java @@ -0,0 +1,61 @@ +package cn.iocoder.yudao.module.iot.service.deviceoperationrecord; + +import cn.iocoder.yudao.framework.common.pojo.PageResult; +import cn.iocoder.yudao.module.iot.controller.admin.deviceoperationrecord.vo.DeviceOperationRecordPageReqVO; +import cn.iocoder.yudao.module.iot.controller.admin.deviceoperationrecord.vo.DeviceOperationRecordSaveReqVO; +import cn.iocoder.yudao.module.iot.controller.admin.deviceoperationrecord.vo.DeviceTotalTimeRecordReqVO; +import cn.iocoder.yudao.module.iot.controller.admin.deviceoperationrecord.vo.DeviceTotalTimeRecordRespVO; +import cn.iocoder.yudao.module.iot.dal.dataobject.deviceoperationrecord.DeviceOperationRecordDO; + +import javax.validation.Valid; +import java.util.List; + +/** + * 运行记录 Service 接口 + * + * @author 必硕科技 + */ +public interface DeviceOperationRecordService { + + /** + * 创建运行记录 + * + * @param createReqVO 创建信息 + * @return 编号 + */ + Long createDeviceOperationRecord(@Valid DeviceOperationRecordSaveReqVO createReqVO); + + /** + * 更新运行记录 + * + * @param updateReqVO 更新信息 + */ + void updateDeviceOperationRecord(@Valid DeviceOperationRecordSaveReqVO updateReqVO); + + /** + * 删除运行记录 + * + * @param id 编号 + */ + void deleteDeviceOperationRecord(Long id); + + /** + * 获得运行记录 + * + * @param id 编号 + * @return 运行记录 + */ + DeviceOperationRecordDO getDeviceOperationRecord(Long id); + + /** + * 获得运行记录分页 + * + * @param pageReqVO 分页查询 + * @return 运行记录分页 + */ + PageResult getDeviceOperationRecordPage(DeviceOperationRecordPageReqVO pageReqVO); + + PageResult deviceOperationPage(@Valid DeviceTotalTimeRecordReqVO pageReqVO); + + List deviceOperationList(@Valid DeviceTotalTimeRecordReqVO pageReqVO); +} \ No newline at end of file diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/deviceoperationrecord/DeviceOperationRecordServiceImpl.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/deviceoperationrecord/DeviceOperationRecordServiceImpl.java new file mode 100644 index 0000000..6eaea78 --- /dev/null +++ b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/deviceoperationrecord/DeviceOperationRecordServiceImpl.java @@ -0,0 +1,395 @@ +package cn.iocoder.yudao.module.iot.service.deviceoperationrecord; + +import cn.iocoder.yudao.framework.common.pojo.PageResult; +import cn.iocoder.yudao.framework.common.util.object.BeanUtils; +import cn.iocoder.yudao.module.iot.controller.admin.deviceoperationrecord.utils.TimeConverterUtil; +import cn.iocoder.yudao.module.iot.controller.admin.deviceoperationrecord.vo.DeviceOperationRecordPageReqVO; +import cn.iocoder.yudao.module.iot.controller.admin.deviceoperationrecord.vo.DeviceOperationRecordSaveReqVO; +import cn.iocoder.yudao.module.iot.controller.admin.deviceoperationrecord.vo.DeviceTotalTimeRecordReqVO; +import cn.iocoder.yudao.module.iot.controller.admin.deviceoperationrecord.vo.DeviceTotalTimeRecordRespVO; +import cn.iocoder.yudao.module.iot.dal.dataobject.deviceoperationrecord.DeviceOperationRecordDO; +import cn.iocoder.yudao.module.iot.dal.mysql.deviceoperationrecord.DeviceOperationRecordMapper; +import cn.iocoder.yudao.module.iot.service.device.TDengineService; +import com.baomidou.mybatisplus.core.metadata.IPage; +import com.baomidou.mybatisplus.core.toolkit.CollectionUtils; +import com.baomidou.mybatisplus.extension.plugins.pagination.Page; +import lombok.extern.slf4j.Slf4j; +import org.apache.commons.lang3.StringUtils; +import org.springframework.stereotype.Service; +import org.springframework.validation.annotation.Validated; + +import javax.annotation.Resource; +import java.time.Duration; +import java.time.LocalDateTime; +import java.time.LocalTime; +import java.time.format.DateTimeFormatter; +import java.util.*; +import java.util.stream.Collectors; + +import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception; +import static cn.iocoder.yudao.module.iot.enums.ErrorCodeConstants.DEVICE_OPERATION_RECORD_NOT_EXISTS; + +/** + * 运行记录 Service 实现类 + * + * @author 必硕科技 + */ +@Service +@Validated +@Slf4j +public class DeviceOperationRecordServiceImpl implements DeviceOperationRecordService { + + @Resource + private DeviceOperationRecordMapper deviceOperationRecordMapper; + @Resource + private TDengineService tDengineService; + + + @Override + public Long createDeviceOperationRecord(DeviceOperationRecordSaveReqVO createReqVO) { + // 插入 + DeviceOperationRecordDO deviceOperationRecord = BeanUtils.toBean(createReqVO, DeviceOperationRecordDO.class); + deviceOperationRecordMapper.insert(deviceOperationRecord); + // 返回 + return deviceOperationRecord.getId(); + } + + @Override + public void updateDeviceOperationRecord(DeviceOperationRecordSaveReqVO updateReqVO) { + // 校验存在 + validateDeviceOperationRecordExists(updateReqVO.getId()); + // 更新 + DeviceOperationRecordDO updateObj = BeanUtils.toBean(updateReqVO, DeviceOperationRecordDO.class); + deviceOperationRecordMapper.updateById(updateObj); + } + + @Override + public void deleteDeviceOperationRecord(Long id) { + // 校验存在 + validateDeviceOperationRecordExists(id); + // 删除 + deviceOperationRecordMapper.deleteById(id); + } + + private void validateDeviceOperationRecordExists(Long id) { + if (deviceOperationRecordMapper.selectById(id) == null) { + throw exception(DEVICE_OPERATION_RECORD_NOT_EXISTS); + } + } + + @Override + public DeviceOperationRecordDO getDeviceOperationRecord(Long id) { + return deviceOperationRecordMapper.selectById(id); + } + + @Override + public PageResult getDeviceOperationRecordPage(DeviceOperationRecordPageReqVO pageReqVO) { + return deviceOperationRecordMapper.selectPage(pageReqVO); + } + + @Override + public PageResult deviceOperationPage(DeviceTotalTimeRecordReqVO pageReqVO) { + + // 1. 先从 MySQL 查询设备分页信息 + Page page = new Page<>(pageReqVO.getPageNo(), pageReqVO.getPageSize()); + IPage mysqlDevicePage = + deviceOperationRecordMapper.selectDevicePageFromMySQL(page, pageReqVO); + + if (mysqlDevicePage == null || mysqlDevicePage.getRecords().isEmpty()) { + return new PageResult<>(Collections.emptyList(), 0L); + } + + // 2. 提取设备ID列表 + List deviceIds = mysqlDevicePage.getRecords().stream() + .map(DeviceTotalTimeRecordRespVO::getId) + .collect(Collectors.toList()); + + // 3. 从 TDengine 批量查询统计数据 + List> statsList = + deviceOperationRecordMapper.selectDeviceStatsFromTD( + deviceIds, + pageReqVO.getStartTime(), + pageReqVO.getEndTime() + ); + + // 4. 转换为 Map<设备ID, 统计数据> + Map tdStatsMap = convertStatsListToMap(statsList); + + + // 5. 合并数据 + List result = new ArrayList<>(); + for (DeviceTotalTimeRecordRespVO device : mysqlDevicePage.getRecords()) { + DeviceTotalTimeRecordRespVO stats = tdStatsMap.get(device.getId()); + if (stats != null) { + // 设置统计数据 + device.setTotalOfflineTime(stats.getTotalOfflineTime()); + device.setTotalRunningTime(stats.getTotalRunningTime()); + device.setTotalStandbyTime(stats.getTotalStandbyTime()); + device.setTotalFaultTime(stats.getTotalFaultTime()); + } else { + // 没有统计数据,设为0 + device.setTotalOfflineTime(0); + device.setTotalRunningTime(0); + device.setTotalStandbyTime(0); + device.setTotalFaultTime(0); + } + result.add(device); + } + + // 5. 计算和转换 + calculateAndSetConvertedValues(result, pageReqVO); + + return new PageResult<>(result, mysqlDevicePage.getTotal()); + + } + + @Override + public List deviceOperationList(DeviceTotalTimeRecordReqVO deviceTotalTimeRecordReqVO) { + + // 1 查询产线设备 + List lineList = deviceOperationRecordMapper.selectLine(); + + // 2 提取 deviceId + String ids = lineList.stream() + .map(DeviceTotalTimeRecordRespVO::getId) + .filter(Objects::nonNull) + .distinct() + .map(String::valueOf) + .collect(Collectors.joining(",")); + + // 3 设置时间 + DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss"); + deviceTotalTimeRecordReqVO.setStartTime(LocalDateTime.now().with(LocalTime.MIN).format(formatter)); + deviceTotalTimeRecordReqVO.setEndTime(LocalDateTime.now().format(formatter)); + deviceTotalTimeRecordReqVO.setIds(ids); + +// // 4 查询统计数据 +// List list = +// deviceOperationRecordMapper.deviceOperationList(deviceTotalTimeRecordReqVO); + + + // 4.1 先从 MySQL 查询设备列表 + List deviceList = + deviceOperationRecordMapper.selectDeviceListFromMySQL(deviceTotalTimeRecordReqVO); + + if (CollectionUtils.isEmpty(deviceList)) { + return Collections.emptyList(); + } + + // 4.2 提取设备ID列表 + List deviceIds = deviceList.stream() + .map(DeviceTotalTimeRecordRespVO::getId) + .collect(Collectors.toList()); + + // 4.3 从 TDengine 批量查询设备运行时间统计 + Map statsMap = + tDengineService.getDeviceTimeStatsFromTD(deviceIds, deviceTotalTimeRecordReqVO.getStartTime(), deviceTotalTimeRecordReqVO.getEndTime()); + + // 4.4 合并数据 + List list = new ArrayList<>(); + for (DeviceTotalTimeRecordRespVO device : deviceList) { + DeviceTotalTimeRecordRespVO stats = statsMap.get(device.getId()); + if (stats != null) { + // 设置统计数据 + device.setTotalOfflineTime(stats.getTotalOfflineTime()); + device.setTotalRunningTime(stats.getTotalRunningTime()); + device.setTotalStandbyTime(stats.getTotalStandbyTime()); + device.setTotalFaultTime(stats.getTotalFaultTime()); + } else { + // 没有统计数据,设为0 + device.setTotalOfflineTime(0); + device.setTotalRunningTime(0); + device.setTotalStandbyTime(0); + device.setTotalFaultTime(0); + } + list.add(device); + } + + + if (!list.isEmpty()) { + + + // 5 计算转换 + calculateAndSetConvertedValues(list, deviceTotalTimeRecordReqVO); + + // 6 转成 map (deviceId -> 统计数据) + Map recordMap = + list.stream().collect(Collectors.toMap( + DeviceTotalTimeRecordRespVO::getId, + v -> v, + (a, b) -> a + )); + + // 7 回填到 lineList + for (DeviceTotalTimeRecordRespVO line : lineList) { + + DeviceTotalTimeRecordRespVO record = recordMap.get(line.getId()); + + if (record != null) { + line.setTotalOfflineTime(record.getTotalOfflineTime()); + line.setTotalRunningTime(record.getTotalRunningTime()); + line.setTotalStandbyTime(record.getTotalStandbyTime()); + line.setTotalFaultTime(record.getTotalFaultTime()); + line.setPowerOnRate(record.getPowerOnRate()); + line.setUtilizationRate(record.getUtilizationRate()); + + } + } + } + + // 8 返回产线设备 + return lineList; + } + + private void calculateAndSetConvertedValues( + List records, + DeviceTotalTimeRecordReqVO reqVO) { + + for (DeviceTotalTimeRecordRespVO record : records) { + + try { + + // 1设置查询时间 + String startTimeStr = reqVO.getStartTime(); + String endTimeStr = reqVO.getEndTime(); + + if (StringUtils.isNotBlank(startTimeStr)) { + record.setStartTime(startTimeStr); + } + if (StringUtils.isNotBlank(endTimeStr)) { + record.setEndTime(endTimeStr); + } + + // 获取原始秒数 + double offlineSec = record.getTotalOfflineTime(); + double runningSec = record.getTotalRunningTime(); + double standbySec = record.getTotalStandbyTime(); + double faultSec = record.getTotalFaultTime(); + + // 在线时间 = 运行 + 待机 + 故障 + double onlineSec = runningSec + standbySec + faultSec; + + // 计算总时间(根据筛选时间) + double totalSec = 0; + if (StringUtils.isNotBlank(startTimeStr) && StringUtils.isNotBlank(endTimeStr)) { + + DateTimeFormatter formatter = + DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss"); + + LocalDateTime start = LocalDateTime.parse(startTimeStr, formatter); + LocalDateTime end = LocalDateTime.parse(endTimeStr, formatter); + + totalSec = Duration.between(start, end).getSeconds(); + } + + // 防止负数或异常 + if (totalSec < 0) { + totalSec = 0; + } + + // 计算开机率 + double powerOnRate = 0; + if (totalSec > 0) { + powerOnRate = onlineSec / totalSec; + } + + // 计算稼动率 + double utilizationRate = 0; + if (onlineSec > 0) { + utilizationRate = runningSec / onlineSec; + } + + // 秒转小时(保留2位) + record.setTotalOfflineTime(TimeConverterUtil.secondsToHours(offlineSec, 2)); + record.setTotalRunningTime(TimeConverterUtil.secondsToHours(runningSec, 2)); + record.setTotalStandbyTime(TimeConverterUtil.secondsToHours(standbySec, 2)); + record.setTotalFaultTime(TimeConverterUtil.secondsToHours(faultSec, 2)); + + // 百分比字符串 + record.setPowerOnRate(TimeConverterUtil.getPercentString(powerOnRate)); + record.setUtilizationRate(TimeConverterUtil.getPercentString(utilizationRate)); + + } catch (Exception e) { + log.error("计算设备{}时间统计出错: {}", record.getDeviceCode(), e.getMessage()); + setDefaultValues(record); + } + } + } + private void setDefaultValues(DeviceTotalTimeRecordRespVO record) { + record.setTotalRunningTime(0.0); + record.setTotalStandbyTime(0.0); + record.setTotalFaultTime(0.0); + record.setTotalWarningTime(0.0); + record.setUtilizationRate("0%"); + } + + + /** + * 将统计列表转换为 Map<设备ID, 统计数据> + */ + private Map convertStatsListToMap(List> statsList) { + if (CollectionUtils.isEmpty(statsList)) { + return Collections.emptyMap(); + } + + Map statsMap = new HashMap<>(); + + for (Map stat : statsList) { + Object deviceIdObj = stat.get("deviceid"); + if (deviceIdObj == null) { + continue; + } + + Long deviceId = null; + if (deviceIdObj instanceof Number) { + deviceId = ((Number) deviceIdObj).longValue(); + } else if (deviceIdObj instanceof String) { + try { + deviceId = Long.parseLong((String) deviceIdObj); + } catch (NumberFormatException e) { + log.warn("设备ID格式错误: {}", deviceIdObj); + continue; + } + } else { + log.warn("设备ID类型错误: {}", deviceIdObj.getClass()); + continue; + } + + DeviceTotalTimeRecordRespVO vo = new DeviceTotalTimeRecordRespVO(); + vo.setId(deviceId); + + // 安全设置统计值 + vo.setTotalOfflineTime(getIntValue(stat, "totalofflinetime")); + vo.setTotalRunningTime(getIntValue(stat, "totalrunningtime")); + vo.setTotalStandbyTime(getIntValue(stat, "totalstandbytime")); + vo.setTotalFaultTime(getIntValue(stat, "totalfaulttime")); + + statsMap.put(deviceId, vo); + } + + return statsMap; + } + + /** + * 安全获取整数值 + */ + private Integer getIntValue(Map map, String key) { + Object value = map.get(key.toLowerCase()); + if (value == null) { + return 0; + } + + if (value instanceof Number) { + return ((Number) value).intValue(); + } else if (value instanceof String) { + try { + return Integer.parseInt((String) value); + } catch (NumberFormatException e) { + log.warn("{} 值格式错误: {}", key, value); + return 0; + } + } + + return 0; + } +} \ No newline at end of file diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/devicepointrules/DevicePointRulesService.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/devicepointrules/DevicePointRulesService.java new file mode 100644 index 0000000..b6d0fac --- /dev/null +++ b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/devicepointrules/DevicePointRulesService.java @@ -0,0 +1,56 @@ +package cn.iocoder.yudao.module.iot.service.devicepointrules; + +import cn.iocoder.yudao.framework.common.pojo.PageResult; +import cn.iocoder.yudao.module.iot.controller.admin.devicepointrules.vo.*; +import cn.iocoder.yudao.module.iot.dal.dataobject.devicepointrules.DevicePointRulesDO; + +import javax.validation.Valid; +import java.util.List; + +/** + * 点位规则 Service 接口 + * + * @author 必硕科技 + */ +public interface DevicePointRulesService { + + /** + * 创建点位规则 + * + * @param createReqVO 创建信息 + * @return 编号 + */ + Long createDevicePointRules(@Valid DevicePointRulesSaveReqVO createReqVO); + + /** + * 更新点位规则 + * + * @param updateReqVO 更新信息 + */ + void updateDevicePointRules(@Valid DevicePointRulesSaveReqVO updateReqVO); + + /** + * 删除点位规则 + * + * @param id 编号 + */ + void deleteDevicePointRules(Long id); + + /** + * 获得点位规则 + * + * @param id 编号 + * @return 点位规则 + */ + DevicePointRulesDO getDevicePointRules(Long id); + + /** + * 获得点位规则分页 + * + * @param pageReqVO 分页查询 + * @return 点位规则分页 + */ + PageResult getDevicePointRulesPage(DevicePointRulesPageReqVO pageReqVO); + + List getList(Long id); +} \ No newline at end of file diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/devicepointrules/DevicePointRulesServiceImpl.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/devicepointrules/DevicePointRulesServiceImpl.java new file mode 100644 index 0000000..08b999d --- /dev/null +++ b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/devicepointrules/DevicePointRulesServiceImpl.java @@ -0,0 +1,85 @@ +package cn.iocoder.yudao.module.iot.service.devicepointrules; + +import cn.iocoder.yudao.framework.common.pojo.PageResult; +import cn.iocoder.yudao.framework.common.util.object.BeanUtils; +import cn.iocoder.yudao.module.iot.controller.admin.devicepointrules.vo.*; +import cn.iocoder.yudao.module.iot.dal.dataobject.devicepointrules.DevicePointRulesDO; +import cn.iocoder.yudao.module.iot.dal.mysql.devicepointrules.DevicePointRulesMapper; +import com.alibaba.fastjson.JSON; +import com.baomidou.mybatisplus.core.toolkit.Wrappers; +import org.springframework.stereotype.Service; +import org.springframework.validation.annotation.Validated; + +import javax.annotation.Resource; +import java.util.List; + +import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception; +import static cn.iocoder.yudao.module.iot.enums.ErrorCodeConstants.DEVICE_POINT_RULES_NOT_EXISTS; + +/** + * 点位规则 Service 实现类 + * + * @author 必硕科技 + */ +@Service +@Validated +public class DevicePointRulesServiceImpl implements DevicePointRulesService { + + @Resource + private DevicePointRulesMapper devicePointRulesMapper; + + @Override + public Long createDevicePointRules(DevicePointRulesSaveReqVO createReqVO) { + // 插入 + DevicePointRulesDO devicePointRules = BeanUtils.toBean(createReqVO, DevicePointRulesDO.class); + devicePointRulesMapper.insert(devicePointRules); + // 返回 + return devicePointRules.getId(); + } + + @Override + public void updateDevicePointRules(DevicePointRulesSaveReqVO updateReqVO) { + // 校验存在 + validateDevicePointRulesExists(updateReqVO.getId()); + // 更新 + DevicePointRulesDO updateObj = BeanUtils.toBean(updateReqVO, DevicePointRulesDO.class); + if (!updateReqVO.getPointRulesVOList().isEmpty()){ + String jsonString = JSON.toJSONString(updateReqVO.getPointRulesVOList()); + updateObj.setFieldRule(jsonString); + + } + + devicePointRulesMapper.updateById(updateObj); + } + + @Override + public void deleteDevicePointRules(Long id) { + // 校验存在 + validateDevicePointRulesExists(id); + // 删除 + devicePointRulesMapper.deleteById(id); + } + + private void validateDevicePointRulesExists(Long id) { + if (devicePointRulesMapper.selectById(id) == null) { + throw exception(DEVICE_POINT_RULES_NOT_EXISTS); + } + } + + @Override + public DevicePointRulesDO getDevicePointRules(Long id) { + return devicePointRulesMapper.selectById(id); + } + + @Override + public PageResult getDevicePointRulesPage(DevicePointRulesPageReqVO pageReqVO) { + return devicePointRulesMapper.selectPage(pageReqVO); + } + + @Override + public List getList(Long id) { + return devicePointRulesMapper.selectList(Wrappers.lambdaQuery() + .eq(DevicePointRulesDO::getDeviceId,id).eq(DevicePointRulesDO::getIdentifier,"ALARM").orderByDesc(DevicePointRulesDO::getDeviceId)); + } + +} \ No newline at end of file diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/devicewarinningrecord/DeviceWarinningRecordService.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/devicewarinningrecord/DeviceWarinningRecordService.java new file mode 100644 index 0000000..e197f2f --- /dev/null +++ b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/devicewarinningrecord/DeviceWarinningRecordService.java @@ -0,0 +1,62 @@ +package cn.iocoder.yudao.module.iot.service.devicewarinningrecord; + +import cn.iocoder.yudao.framework.common.pojo.PageResult; +import cn.iocoder.yudao.module.iot.controller.admin.devicewarinningrecord.vo.DeviceWarinningRecordPageReqVO; +import cn.iocoder.yudao.module.iot.controller.admin.devicewarinningrecord.vo.DeviceWarinningRecordSaveReqVO; +import cn.iocoder.yudao.module.iot.dal.dataobject.devicewarinningrecord.DeviceWarinningRecordDO; + +import javax.validation.Valid; +import java.util.List; +import java.util.Map; + +/** + * 告警记录 Service 接口 + * + * @author 必硕科技 + */ +public interface DeviceWarinningRecordService { + + /** + * 创建告警记录 + * + * @param createReqVO 创建信息 + * @return 编号 + */ + Long createDeviceWarinningRecord(@Valid DeviceWarinningRecordSaveReqVO createReqVO); + + /** + * 更新告警记录 + * + * @param updateReqVO 更新信息 + */ + void updateDeviceWarinningRecord(@Valid DeviceWarinningRecordSaveReqVO updateReqVO); + + /** + * 删除告警记录 + * + * @param id 编号 + */ + void deleteDeviceWarinningRecord(Long id); + + /** + * 获得告警记录 + * + * @param id 编号 + * @return 告警记录 + */ + DeviceWarinningRecordDO getDeviceWarinningRecord(Long id); + + /** + * 获得告警记录分页 + * + * @param pageReqVO 分页查询 + * @return 告警记录分页 + */ + PageResult getDeviceWarinningRecordPage(DeviceWarinningRecordPageReqVO pageReqVO); + + List getList(Long id); + + List> getLastSevenHoursCount(); + + +} \ No newline at end of file diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/devicewarinningrecord/DeviceWarinningRecordServiceImpl.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/devicewarinningrecord/DeviceWarinningRecordServiceImpl.java new file mode 100644 index 0000000..4ad3c5b --- /dev/null +++ b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/devicewarinningrecord/DeviceWarinningRecordServiceImpl.java @@ -0,0 +1,145 @@ +package cn.iocoder.yudao.module.iot.service.devicewarinningrecord; + +import cn.iocoder.yudao.framework.common.pojo.PageResult; +import cn.iocoder.yudao.framework.common.util.object.BeanUtils; +import cn.iocoder.yudao.module.iot.controller.admin.devicewarinningrecord.vo.DeviceWarinningRecordPageReqVO; +import cn.iocoder.yudao.module.iot.controller.admin.devicewarinningrecord.vo.DeviceWarinningRecordSaveReqVO; +import cn.iocoder.yudao.module.iot.dal.dataobject.devicewarinningrecord.DeviceWarinningRecordDO; +import cn.iocoder.yudao.module.iot.dal.mysql.devicewarinningrecord.DeviceWarinningRecordMapper; +import cn.iocoder.yudao.module.iot.service.device.TDengineService; +import org.springframework.stereotype.Service; +import org.springframework.validation.annotation.Validated; + +import javax.annotation.Resource; +import java.time.LocalDateTime; +import java.time.format.DateTimeFormatter; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.stream.Collectors; + +import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception; +import static cn.iocoder.yudao.module.iot.enums.ErrorCodeConstants.DEVICE_WARINNING_RECORD_NOT_EXISTS; + +/** + * 告警记录 Service 实现类 + * + * @author 必硕科技 + */ +@Service +@Validated +public class DeviceWarinningRecordServiceImpl implements DeviceWarinningRecordService { + + @Resource + private DeviceWarinningRecordMapper deviceWarinningRecordMapper; + + @Resource + private TDengineService tDengineService; + + + @Override + public Long createDeviceWarinningRecord(DeviceWarinningRecordSaveReqVO createReqVO) { + // 插入 + DeviceWarinningRecordDO deviceWarinningRecord = BeanUtils.toBean(createReqVO, DeviceWarinningRecordDO.class); + deviceWarinningRecordMapper.insert(deviceWarinningRecord); + // 返回 + return deviceWarinningRecord.getId(); + } + + @Override + public void updateDeviceWarinningRecord(DeviceWarinningRecordSaveReqVO updateReqVO) { + // 校验存在 + validateDeviceWarinningRecordExists(updateReqVO.getId()); + // 更新 + DeviceWarinningRecordDO updateObj = BeanUtils.toBean(updateReqVO, DeviceWarinningRecordDO.class); + deviceWarinningRecordMapper.updateById(updateObj); + } + + @Override + public void deleteDeviceWarinningRecord(Long id) { + // 校验存在 + validateDeviceWarinningRecordExists(id); + // 删除 + deviceWarinningRecordMapper.deleteById(id); + } + + private void validateDeviceWarinningRecordExists(Long id) { + if (deviceWarinningRecordMapper.selectById(id) == null) { + throw exception(DEVICE_WARINNING_RECORD_NOT_EXISTS); + } + } + + @Override + public DeviceWarinningRecordDO getDeviceWarinningRecord(Long id) { + return deviceWarinningRecordMapper.selectById(id); + } + + @Override + public PageResult getDeviceWarinningRecordPage(DeviceWarinningRecordPageReqVO pageReqVO) { +// return deviceWarinningRecordMapper.selectPage(pageReqVO); + return tDengineService.selectRunngingRecordPage(pageReqVO); + } + + @Override + public List getList(Long id) { +// if (id == null) { +// return Collections.emptyList(); +// } +// return deviceWarinningRecordMapper.selectList( +// Wrappers.lambdaQuery() +// .eq(id != null, DeviceWarinningRecordDO::getDeviceId, id) +// .orderByDesc(DeviceWarinningRecordDO::getCreateTime) +// .last("LIMIT 100")// 限制 100 条 +// ); + + return tDengineService.selectDeviceWarningList(id); + } + + @Override + public List> getLastSevenHoursCount() { + + // 查询近七个小时数据 + List> result = deviceWarinningRecordMapper.getLastSevenHoursCount(); + + // 补全最近7个小时的所有时间段(即使没有告警也显示0) + return fillMissingHours(result); + + + } + + + /** + * 补全最近7个小时的所有时间段 + */ + private List> fillMissingHours(List> dbResult) { + // 创建结果列表 + List> result = new ArrayList<>(); + + // 获取当前时间 + LocalDateTime now = LocalDateTime.now(); + DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:00"); + + // 将数据库查询结果转换为以hour为key的Map + Map countMap = dbResult.stream() + .collect(Collectors.toMap( + map -> (String) map.get("hour"), + map -> (Long) map.get("count") + )); + + // 生成最近7个小时的时间段 + for (int i = 6; i >= 0; i--) { + LocalDateTime hourTime = now.minusHours(i); + String hourStr = hourTime.format(formatter); + + Map hourData = new HashMap<>(); + hourData.put("hour", hourStr); + hourData.put("count", countMap.getOrDefault(hourStr, 0L)); +// hourData.put("hourTime", hourTime.toString()); + + result.add(hourData); + } + + return result; + } +} \ No newline at end of file diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/gateway/GatewayService.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/gateway/GatewayService.java new file mode 100644 index 0000000..e9258c2 --- /dev/null +++ b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/gateway/GatewayService.java @@ -0,0 +1,102 @@ +package cn.iocoder.yudao.module.iot.service.gateway; + +import cn.iocoder.yudao.framework.common.pojo.PageParam; +import cn.iocoder.yudao.framework.common.pojo.PageResult; +import cn.iocoder.yudao.module.iot.controller.admin.gateway.vo.GatewayPageReqVO; +import cn.iocoder.yudao.module.iot.controller.admin.gateway.vo.GatewaySaveReqVO; +import cn.iocoder.yudao.module.iot.dal.dataobject.device.DeviceDO; +import cn.iocoder.yudao.module.iot.dal.dataobject.gateway.GatewayDO; + +import javax.validation.Valid; +import java.util.List; + +/** + * 网关 Service 接口 + * + * @author 内蒙必硕 + */ +public interface GatewayService { + + /** + * 创建网关 + * + * @param createReqVO 创建信息 + * @return 编号 + */ + Long createGateway(@Valid GatewaySaveReqVO createReqVO); + + /** + * 更新网关 + * + * @param updateReqVO 更新信息 + */ + void updateGateway(@Valid GatewaySaveReqVO updateReqVO); + + /** + * 删除网关 + * + * @param id 编号 + */ + void deleteGateway(Long id); + + /** + * 获得网关 + * + * @param id 编号 + * @return 网关 + */ + GatewayDO getGateway(Long id); + + List selectListByIsEnable(boolean isEnable); + + /** + * 获得网关分页 + * + * @param pageReqVO 分页查询 + * @return 网关分页 + */ + PageResult getGatewayPage(GatewayPageReqVO pageReqVO); + + // ==================== 子表(物联设备) ==================== + + /** + * 获得物联设备分页 + * + * @param pageReqVO 分页查询 + * @param gatewayId 网关id + * @return 物联设备分页 + */ + PageResult getDevicePage(PageParam pageReqVO, Long gatewayId); + + /** + * 创建物联设备 + * + * @param device 创建信息 + * @return 编号 + */ + Long createDevice(@Valid DeviceDO device); + + /** + * 更新物联设备 + * + * @param device 更新信息 + */ + void updateDevice(@Valid DeviceDO device); + + /** + * 删除物联设备 + * + * @param id 编号 + */ + void deleteDevice(Long id); + + /** + * 获得物联设备 + * + * @param id 编号 + * @return 物联设备 + */ + DeviceDO getDevice(Long id); + + +} \ No newline at end of file diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/gateway/GatewayServiceImpl.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/gateway/GatewayServiceImpl.java new file mode 100644 index 0000000..8f12c18 --- /dev/null +++ b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/gateway/GatewayServiceImpl.java @@ -0,0 +1,133 @@ +package cn.iocoder.yudao.module.iot.service.gateway; + +import cn.iocoder.yudao.framework.common.pojo.PageParam; +import cn.iocoder.yudao.framework.common.pojo.PageResult; +import cn.iocoder.yudao.framework.common.util.object.BeanUtils; +import cn.iocoder.yudao.module.iot.controller.admin.gateway.vo.GatewayPageReqVO; +import cn.iocoder.yudao.module.iot.controller.admin.gateway.vo.GatewaySaveReqVO; +import cn.iocoder.yudao.module.iot.dal.dataobject.device.DeviceDO; +import cn.iocoder.yudao.module.iot.dal.dataobject.gateway.GatewayDO; +import cn.iocoder.yudao.module.iot.dal.mysql.device.DeviceMapper; +import cn.iocoder.yudao.module.iot.dal.mysql.gateway.GatewayMapper; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; +import org.springframework.validation.annotation.Validated; + +import javax.annotation.Resource; +import java.util.List; + +import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception; +import static cn.iocoder.yudao.module.iot.enums.ErrorCodeConstants.DEVICE_NOT_EXISTS; +import static cn.iocoder.yudao.module.iot.enums.ErrorCodeConstants.GATEWAY_NOT_EXISTS; + +/** + * 网关 Service 实现类 + * + * @author 内蒙必硕 + */ +@Service +@Validated +public class GatewayServiceImpl implements GatewayService { + + @Resource + private GatewayMapper gatewayMapper; + @Resource + private DeviceMapper deviceMapper; + + + @Override + public Long createGateway(GatewaySaveReqVO createReqVO) { + // 插入 + GatewayDO gateway = BeanUtils.toBean(createReqVO, GatewayDO.class); + gatewayMapper.insert(gateway); + // 返回 + return gateway.getId(); + } + + @Override + public void updateGateway(GatewaySaveReqVO updateReqVO) { + // 校验存在 + validateGatewayExists(updateReqVO.getId()); + // 更新 + GatewayDO updateObj = BeanUtils.toBean(updateReqVO, GatewayDO.class); + gatewayMapper.updateById(updateObj); + } + + @Override + @Transactional(rollbackFor = Exception.class) + public void deleteGateway(Long id) { + // 校验存在 + validateGatewayExists(id); + // 删除 + gatewayMapper.deleteById(id); + + // 删除子表 + deleteDeviceByGatewayId(id); + } + + private void validateGatewayExists(Long id) { + if (gatewayMapper.selectById(id) == null) { + throw exception(GATEWAY_NOT_EXISTS); + } + } + + @Override + public GatewayDO getGateway(Long id) { + return gatewayMapper.selectById(id); + } + + @Override + public List selectListByIsEnable(boolean isEnable) { + return gatewayMapper.selectListByIsEnable(isEnable); + } + + @Override + public PageResult getGatewayPage(GatewayPageReqVO pageReqVO) { + return gatewayMapper.selectPage(pageReqVO); + } + + // ==================== 子表(物联设备) ==================== + + @Override + public PageResult getDevicePage(PageParam pageReqVO, Long gatewayId) { + return deviceMapper.selectPage(pageReqVO, gatewayId); + } + + @Override + public Long createDevice(DeviceDO device) { + deviceMapper.insert(device); + return device.getId(); + } + + @Override + public void updateDevice(DeviceDO device) { + // 校验存在 + validateDeviceExists(device.getId()); + // 更新 + deviceMapper.updateById(device); + } + + @Override + public void deleteDevice(Long id) { + // 校验存在 + validateDeviceExists(id); + // 删除 + deviceMapper.deleteById(id); + } + + @Override + public DeviceDO getDevice(Long id) { + return deviceMapper.selectById(id); + } + + private void validateDeviceExists(Long id) { + if (deviceMapper.selectById(id) == null) { + throw exception(DEVICE_NOT_EXISTS); + } + } + + private void deleteDeviceByGatewayId(Long gatewayId) { + deviceMapper.deleteByGatewayId(gatewayId); + } + +} \ No newline at end of file diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/orgnode/OrgNodeService.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/orgnode/OrgNodeService.java new file mode 100644 index 0000000..1c86594 --- /dev/null +++ b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/orgnode/OrgNodeService.java @@ -0,0 +1,64 @@ +package cn.iocoder.yudao.module.iot.service.orgnode; + +import java.util.*; +import javax.validation.*; +import cn.iocoder.yudao.module.iot.controller.admin.orgnode.vo.*; +import cn.iocoder.yudao.module.iot.dal.dataobject.orgnode.OrgNodeDO; +import cn.iocoder.yudao.framework.common.pojo.PageResult; +import cn.iocoder.yudao.framework.common.pojo.PageParam; + +/** + * 客户组织节点表,用于管理客户、车间、产线等多级组织结构 Service 接口 + * + * @author 管理员 + */ +public interface OrgNodeService { + + /** + * 创建客户组织节点表,用于管理客户、车间、产线等多级组织结构 + * + * @param createReqVO 创建信息 + * @return 编号 + */ + Long createOrgNode(@Valid OrgNodeSaveReqVO createReqVO); + + /** + * 更新客户组织节点表,用于管理客户、车间、产线等多级组织结构 + * + * @param updateReqVO 更新信息 + */ + void updateOrgNode(@Valid OrgNodeSaveReqVO updateReqVO); + + /** + * 删除客户组织节点表,用于管理客户、车间、产线等多级组织结构 + * + * @param id 编号 + */ + void deleteOrgNode(Long id); + + /** + * 批量删除客户组织节点表,用于管理客户、车间、产线等多级组织结构 + * + * @param ids 编号 + */ + void deleteOrgNodeListByIds(List ids); + + /** + * 获得客户组织节点表,用于管理客户、车间、产线等多级组织结构 + * + * @param id 编号 + * @return 客户组织节点表,用于管理客户、车间、产线等多级组织结构 + */ + OrgNodeDO getOrgNode(Long id); + + /** + * 获得客户组织节点表,用于管理客户、车间、产线等多级组织结构分页 + * + * @param pageReqVO 分页查询 + * @return 客户组织节点表,用于管理客户、车间、产线等多级组织结构分页 + */ + PageResult getOrgNodePage(OrgNodePageReqVO pageReqVO); + + List getOrgTree(Long customerId); + +} \ No newline at end of file diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/orgnode/OrgNodeServiceImpl.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/orgnode/OrgNodeServiceImpl.java new file mode 100644 index 0000000..7787920 --- /dev/null +++ b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/orgnode/OrgNodeServiceImpl.java @@ -0,0 +1,122 @@ +package cn.iocoder.yudao.module.iot.service.orgnode; + +import cn.hutool.core.collection.CollUtil; +import cn.iocoder.yudao.framework.tenant.core.context.TenantContextHolder; +import org.springframework.stereotype.Service; +import javax.annotation.Resource; +import org.springframework.validation.annotation.Validated; +import org.springframework.transaction.annotation.Transactional; + +import java.util.*; +import cn.iocoder.yudao.module.iot.controller.admin.orgnode.vo.*; +import cn.iocoder.yudao.module.iot.dal.dataobject.orgnode.OrgNodeDO; +import cn.iocoder.yudao.framework.common.pojo.PageResult; +import cn.iocoder.yudao.framework.common.pojo.PageParam; +import cn.iocoder.yudao.framework.common.util.object.BeanUtils; + +import cn.iocoder.yudao.module.iot.dal.mysql.orgnode.OrgNodeMapper; + +import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception; +import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertList; +import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.diffList; +import static cn.iocoder.yudao.module.iot.enums.ErrorCodeConstants.*; + +/** + * 客户组织节点表,用于管理客户、车间、产线等多级组织结构 Service 实现类 + * + * @author 管理员 + */ +@Service +@Validated +public class OrgNodeServiceImpl implements OrgNodeService { + + @Resource + private OrgNodeMapper orgNodeMapper; + + @Override + public Long createOrgNode(OrgNodeSaveReqVO createReqVO) { + // 插入 + OrgNodeDO orgNode = BeanUtils.toBean(createReqVO, OrgNodeDO.class); + orgNodeMapper.insert(orgNode); + + // 返回 + return orgNode.getId(); + } + + @Override + public void updateOrgNode(OrgNodeSaveReqVO updateReqVO) { + // 校验存在 + validateOrgNodeExists(updateReqVO.getId()); + // 更新 + OrgNodeDO updateObj = BeanUtils.toBean(updateReqVO, OrgNodeDO.class); + orgNodeMapper.updateById(updateObj); + } + + @Override + public void deleteOrgNode(Long id) { + // 校验存在 + validateOrgNodeExists(id); + // 删除 + orgNodeMapper.deleteById(id); + } + + @Override + public void deleteOrgNodeListByIds(List ids) { + // 删除 + orgNodeMapper.deleteByIds(ids); + } + + + private void validateOrgNodeExists(Long id) { + if (orgNodeMapper.selectById(id) == null) { + throw exception(ORG_NODE_NOT_EXISTS); + } + } + + @Override + public OrgNodeDO getOrgNode(Long id) { + return orgNodeMapper.selectById(id); + } + + @Override + public PageResult getOrgNodePage(OrgNodePageReqVO pageReqVO) { + return orgNodeMapper.selectPage(pageReqVO); + } + + + @Override + public List getOrgTree(Long customerId) { + Long tenantId = TenantContextHolder.getRequiredTenantId(); + List list = orgNodeMapper.selectListByTenantAndCustomer(tenantId, customerId); + if (list.isEmpty()) { + return Collections.emptyList(); + } + + Map map = new HashMap<>(list.size()); + for (OrgNodeDO item : list) { + OrgNodeTreeRespVO vo = new OrgNodeTreeRespVO(); + vo.setId(item.getId()); + vo.setParentId(item.getParentId()); + vo.setNodeType(item.getNodeType()); // 1客户 2车间 3产线 + vo.setName(item.getName()); + vo.setChildren(new ArrayList<>()); + map.put(item.getId(), vo); + } + + List roots = new ArrayList<>(); + for (OrgNodeDO item : list) { + OrgNodeTreeRespVO current = map.get(item.getId()); + if (item.getParentId() == 0) { + roots.add(current); + } else { + OrgNodeTreeRespVO parent = map.get(item.getParentId()); + if (parent != null) { + parent.getChildren().add(current); + } + } + } + return roots; + } + + +} \ No newline at end of file diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/ota/IotOtaFirmwareService.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/ota/IotOtaFirmwareService.java deleted file mode 100644 index 476e4d5..0000000 --- a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/ota/IotOtaFirmwareService.java +++ /dev/null @@ -1,90 +0,0 @@ -package cn.iocoder.yudao.module.iot.service.ota; - -import cn.iocoder.yudao.framework.common.pojo.PageResult; -import cn.iocoder.yudao.module.iot.controller.admin.ota.vo.firmware.IotOtaFirmwareCreateReqVO; -import cn.iocoder.yudao.module.iot.controller.admin.ota.vo.firmware.IotOtaFirmwarePageReqVO; -import cn.iocoder.yudao.module.iot.controller.admin.ota.vo.firmware.IotOtaFirmwareUpdateReqVO; -import cn.iocoder.yudao.module.iot.dal.dataobject.ota.IotOtaFirmwareDO; - -import javax.validation.Valid; - -import java.util.Collection; -import java.util.List; -import java.util.Map; - -import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertMap; - -/** - * OTA 固件管理 Service 接口 - * - * @author Shelly Chan - */ -public interface IotOtaFirmwareService { - - /** - * 创建 OTA 固件 - * - * @param saveReqVO 固件信息 - * @return 固件编号 - */ - Long createOtaFirmware(@Valid IotOtaFirmwareCreateReqVO saveReqVO); - - /** - * 更新 OTA 固件信息 - * - * @param updateReqVO 固件信息 - */ - void updateOtaFirmware(@Valid IotOtaFirmwareUpdateReqVO updateReqVO); - - /** - * 获取 OTA 固件信息 - * - * @param id 固件编号 - * @return 固件信息 - */ - IotOtaFirmwareDO getOtaFirmware(Long id); - - /** - * 根据产品、版本号,获取 OTA 固件信息 - * - * @param productId 产品编号 - * @param version 版本号 - * @return OTA 固件信息 - */ - IotOtaFirmwareDO getOtaFirmwareByProductIdAndVersion(Long productId, String version); - - /** - * 获取 OTA 固件信息列表 - * - * @param ids 固件编号集合 - * @return 固件信息列表 - */ - List getOtaFirmwareList(Collection ids); - - /** - * 获取 OTA 固件信息 Map - * - * @param ids 固件编号集合 - * @return 固件信息 Map - */ - default Map getOtaFirmwareMap(Collection ids) { - return convertMap(getOtaFirmwareList(ids), IotOtaFirmwareDO::getId); - } - - /** - * 分页查询 OTA 固件信息 - * - * @param pageReqVO 分页查询条件 - * @return 分页结果 - */ - PageResult getOtaFirmwarePage(@Valid IotOtaFirmwarePageReqVO pageReqVO); - - /** - * 验证 OTA 固件是否存在 - * - * @param id 固件编号 - * @return 固件信息 - */ - IotOtaFirmwareDO validateFirmwareExists(Long id); - -} diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/ota/IotOtaFirmwareServiceImpl.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/ota/IotOtaFirmwareServiceImpl.java deleted file mode 100644 index 9afd392..0000000 --- a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/ota/IotOtaFirmwareServiceImpl.java +++ /dev/null @@ -1,126 +0,0 @@ -package cn.iocoder.yudao.module.iot.service.ota; - -import cn.hutool.crypto.digest.DigestAlgorithm; -import cn.hutool.crypto.digest.DigestUtil; -import cn.hutool.http.HttpUtil; -import cn.iocoder.yudao.framework.common.pojo.PageResult; -import cn.iocoder.yudao.framework.common.util.object.BeanUtils; -import cn.iocoder.yudao.module.iot.controller.admin.ota.vo.firmware.IotOtaFirmwareCreateReqVO; -import cn.iocoder.yudao.module.iot.controller.admin.ota.vo.firmware.IotOtaFirmwarePageReqVO; -import cn.iocoder.yudao.module.iot.controller.admin.ota.vo.firmware.IotOtaFirmwareUpdateReqVO; -import cn.iocoder.yudao.module.iot.dal.dataobject.ota.IotOtaFirmwareDO; -import cn.iocoder.yudao.module.iot.dal.mysql.ota.IotOtaFirmwareMapper; -import cn.iocoder.yudao.module.iot.service.product.IotProductService; -import lombok.extern.slf4j.Slf4j; -import org.springframework.context.annotation.Lazy; -import org.springframework.stereotype.Service; -import org.springframework.validation.annotation.Validated; - -import javax.annotation.Resource; -import java.io.ByteArrayInputStream; -import java.util.Collection; -import java.util.Collections; -import java.util.List; - -import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception; -import static cn.iocoder.yudao.module.iot.enums.ErrorCodeConstants.OTA_FIRMWARE_NOT_EXISTS; -import static cn.iocoder.yudao.module.iot.enums.ErrorCodeConstants.OTA_FIRMWARE_PRODUCT_VERSION_DUPLICATE; - -/** - * OTA 固件管理 Service 实现类 - * - * @author Shelly Chan - */ -@Service -@Validated -@Slf4j -public class IotOtaFirmwareServiceImpl implements IotOtaFirmwareService { - - @Resource - private IotOtaFirmwareMapper otaFirmwareMapper; - @Lazy - @Resource - private IotProductService productService; - - @Override - public Long createOtaFirmware(IotOtaFirmwareCreateReqVO saveReqVO) { - // 1.1 校验固件产品 + 版本号不能重复 - if (otaFirmwareMapper.selectByProductIdAndVersion(saveReqVO.getProductId(), saveReqVO.getVersion()) != null) { - throw exception(OTA_FIRMWARE_PRODUCT_VERSION_DUPLICATE); - } - // 1.2 校验产品存在 - productService.validateProductExists(saveReqVO.getProductId()); - - // 2. 构建对象 + 存储 - IotOtaFirmwareDO firmware = BeanUtils.toBean(saveReqVO, IotOtaFirmwareDO.class); - // 2.1 计算文件签名等属性 - try { - calculateFileDigest(firmware); - } catch (Exception e) { - log.error("[createOtaFirmware][url({}) 计算文件签名失败]", firmware.getFileUrl(), e); - throw new RuntimeException("计算文件签名失败: " + e.getMessage()); - } - otaFirmwareMapper.insert(firmware); - return firmware.getId(); - } - - @Override - public void updateOtaFirmware(IotOtaFirmwareUpdateReqVO updateReqVO) { - // 1. 校验存在 - validateFirmwareExists(updateReqVO.getId()); - - // 2. 更新数据 - IotOtaFirmwareDO updateObj = BeanUtils.toBean(updateReqVO, IotOtaFirmwareDO.class); - otaFirmwareMapper.updateById(updateObj); - } - - @Override - public IotOtaFirmwareDO getOtaFirmware(Long id) { - return otaFirmwareMapper.selectById(id); - } - - @Override - public IotOtaFirmwareDO getOtaFirmwareByProductIdAndVersion(Long productId, String version) { - return otaFirmwareMapper.selectByProductIdAndVersion(productId, version); - } - - @Override - public List getOtaFirmwareList(Collection ids) { - if (ids == null || ids.isEmpty()) { - return Collections.emptyList(); - } - return otaFirmwareMapper.selectByIds(ids); - } - - @Override - public PageResult getOtaFirmwarePage(IotOtaFirmwarePageReqVO pageReqVO) { - return otaFirmwareMapper.selectPage(pageReqVO); - } - - @Override - public IotOtaFirmwareDO validateFirmwareExists(Long id) { - IotOtaFirmwareDO firmware = otaFirmwareMapper.selectById(id); - if (firmware == null) { - throw exception(OTA_FIRMWARE_NOT_EXISTS); - } - return firmware; - } - - /** - * 计算文件签名 - * - * @param firmware 固件对象 - */ - private void calculateFileDigest(IotOtaFirmwareDO firmware) { - String fileUrl = firmware.getFileUrl(); - // 下载文件并计算签名 - byte[] fileBytes = HttpUtil.downloadBytes(fileUrl); - // 设置文件大小 - firmware.setFileSize((long) fileBytes.length); - // 计算 MD5 签名 - firmware.setFileDigestAlgorithm(DigestAlgorithm.MD5.getValue()); - String md5Hex = DigestUtil.digester(firmware.getFileDigestAlgorithm()).digestHex(new ByteArrayInputStream(fileBytes)); - firmware.setFileDigestValue(md5Hex); - } - -} diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/ota/IotOtaTaskRecordService.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/ota/IotOtaTaskRecordService.java deleted file mode 100644 index df2c93e..0000000 --- a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/ota/IotOtaTaskRecordService.java +++ /dev/null @@ -1,103 +0,0 @@ -package cn.iocoder.yudao.module.iot.service.ota; - -import cn.iocoder.yudao.framework.common.pojo.PageResult; -import cn.iocoder.yudao.module.iot.controller.admin.ota.vo.task.record.IotOtaTaskRecordPageReqVO; -import cn.iocoder.yudao.module.iot.core.mq.message.IotDeviceMessage; -import cn.iocoder.yudao.module.iot.dal.dataobject.device.IotDeviceDO; -import cn.iocoder.yudao.module.iot.dal.dataobject.ota.IotOtaFirmwareDO; -import cn.iocoder.yudao.module.iot.dal.dataobject.ota.IotOtaTaskRecordDO; - -import javax.validation.Valid; -import java.util.List; -import java.util.Map; -import java.util.Set; - -/** - * IoT OTA 升级记录 Service 接口 - */ -public interface IotOtaTaskRecordService { - - /** - * 批量创建 OTA 升级记录 - * - * @param devices 设备列表 - * @param firmwareId 固件编号 - * @param taskId 任务编号 - */ - void createOtaTaskRecordList(List devices, Long firmwareId, Long taskId); - - /** - * 获取 OTA 升级记录的状态统计 - * - * @param firmwareId 固件编号 - * @param taskId 任务编号 - * @return 状态统计 Map,key 为状态码,value 为对应状态的升级记录数量 - */ - Map getOtaTaskRecordStatusStatistics(Long firmwareId, Long taskId); - - /** - * 获取 OTA 升级记录 - * - * @param id 编号 - * @return OTA 升级记录 - */ - IotOtaTaskRecordDO getOtaTaskRecord(Long id); - - /** - * 获取 OTA 升级记录分页 - * - * @param pageReqVO 分页查询 - * @return OTA 升级记录分页 - */ - PageResult getOtaTaskRecordPage(@Valid IotOtaTaskRecordPageReqVO pageReqVO); - - /** - * 根据 OTA 任务编号,取消未结束的升级记录 - * - * @param taskId 升级任务编号 - */ - void cancelTaskRecordListByTaskId(Long taskId); - - /** - * 根据设备编号和记录状态,获取 OTA 升级记录列表 - * - * @param deviceIds 设备编号集合 - * @param statuses 记录状态集合 - * @return OTA 升级记录列表 - */ - List getOtaTaskRecordListByDeviceIdAndStatus(Set deviceIds, Set statuses); - - /** - * 根据记录状态,获取 OTA 升级记录列表 - * - * @param status 升级记录状态 - * @return 升级记录列表 - */ - List getOtaRecordListByStatus(Integer status); - - /** - * 取消 OTA 升级记录 - * - * @param id 记录编号 - */ - void cancelOtaTaskRecord(Long id); - - /** - * 推送 OTA 升级任务记录 - * - * @param record 任务记录 - * @param fireware 固件信息 - * @param device 设备信息 - * @return 是否推送成功 - */ - boolean pushOtaTaskRecord(IotOtaTaskRecordDO record, IotOtaFirmwareDO fireware, IotDeviceDO device); - - /** - * 更新 OTA 升级记录进度 - * - * @param device 设备信息 - * @param message 设备消息 - */ - void updateOtaRecordProgress(IotDeviceDO device, IotDeviceMessage message); - -} diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/ota/IotOtaTaskRecordServiceImpl.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/ota/IotOtaTaskRecordServiceImpl.java deleted file mode 100644 index a993c1b..0000000 --- a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/ota/IotOtaTaskRecordServiceImpl.java +++ /dev/null @@ -1,234 +0,0 @@ -package cn.iocoder.yudao.module.iot.service.ota; - -import cn.hutool.core.collection.CollUtil; -import cn.hutool.core.convert.Convert; -import cn.hutool.core.lang.Assert; -import cn.hutool.core.util.StrUtil; -import cn.iocoder.yudao.framework.common.pojo.PageResult; -import cn.iocoder.yudao.framework.common.util.json.JsonUtils; -import cn.iocoder.yudao.framework.common.util.object.BeanUtils; -import cn.iocoder.yudao.module.iot.controller.admin.ota.vo.task.record.IotOtaTaskRecordPageReqVO; -import cn.iocoder.yudao.module.iot.core.enums.IotDeviceMessageMethodEnum; -import cn.iocoder.yudao.module.iot.core.mq.message.IotDeviceMessage; -import cn.iocoder.yudao.module.iot.core.topic.ota.IotDeviceOtaProgressReqDTO; -import cn.iocoder.yudao.module.iot.core.topic.ota.IotDeviceOtaUpgradeReqDTO; -import cn.iocoder.yudao.module.iot.dal.dataobject.device.IotDeviceDO; -import cn.iocoder.yudao.module.iot.dal.dataobject.ota.IotOtaFirmwareDO; -import cn.iocoder.yudao.module.iot.dal.dataobject.ota.IotOtaTaskRecordDO; -import cn.iocoder.yudao.module.iot.dal.mysql.ota.IotOtaTaskRecordMapper; -import cn.iocoder.yudao.module.iot.enums.ota.IotOtaTaskRecordStatusEnum; -import cn.iocoder.yudao.module.iot.service.device.IotDeviceService; -import cn.iocoder.yudao.module.iot.service.device.message.IotDeviceMessageService; -import lombok.extern.slf4j.Slf4j; -import org.springframework.stereotype.Service; -import org.springframework.transaction.annotation.Transactional; -import org.springframework.validation.annotation.Validated; - -import javax.annotation.Resource; -import java.util.*; - -import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception; -import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.*; -import static cn.iocoder.yudao.module.iot.enums.ErrorCodeConstants.*; - -/** - * OTA 升级任务记录 Service 实现类 - */ -@Service -@Validated -@Slf4j -public class IotOtaTaskRecordServiceImpl implements IotOtaTaskRecordService { - - @Resource - private IotOtaTaskRecordMapper otaTaskRecordMapper; - - @Resource - private IotOtaFirmwareService otaFirmwareService; - @Resource - private IotOtaTaskService otaTaskService; - @Resource - private IotDeviceMessageService deviceMessageService; - @Resource - private IotDeviceService deviceService; - - @Override - public void createOtaTaskRecordList(List devices, Long firmwareId, Long taskId) { - List records = convertList(devices, device -> - IotOtaTaskRecordDO.builder().firmwareId(firmwareId).taskId(taskId) - .deviceId(device.getId()).fromFirmwareId(Convert.toLong(device.getFirmwareId())) - .status(IotOtaTaskRecordStatusEnum.PENDING.getStatus()).progress(0).build()); - otaTaskRecordMapper.insertBatch(records); - } - - @Override - public Map getOtaTaskRecordStatusStatistics(Long firmwareId, Long taskId) { - // 按照 status 枚举,初始化 countMap 为 0 - Map countMap = convertMap(Arrays.asList(IotOtaTaskRecordStatusEnum.values()), - IotOtaTaskRecordStatusEnum::getStatus, iotOtaTaskRecordStatusEnum -> 0L); - - // 查询记录,只返回 id、status 字段 - List records = otaTaskRecordMapper.selectListByFirmwareIdAndTaskId(firmwareId, taskId); - Map> deviceStatusesMap = convertMultiMap(records, - IotOtaTaskRecordDO::getDeviceId, IotOtaTaskRecordDO::getStatus); - // 找到第一个匹配的优先级状态,避免重复计算 - deviceStatusesMap.forEach((deviceId, statuses) -> { - for (Integer priorityStatus : IotOtaTaskRecordStatusEnum.PRIORITY_STATUSES) { - if (statuses.contains(priorityStatus)) { - countMap.put(priorityStatus, countMap.get(priorityStatus) + 1); - return; - } - } - }); - return countMap; - } - - @Override - public IotOtaTaskRecordDO getOtaTaskRecord(Long id) { - return otaTaskRecordMapper.selectById(id); - } - - @Override - public PageResult getOtaTaskRecordPage(IotOtaTaskRecordPageReqVO pageReqVO) { - return otaTaskRecordMapper.selectPage(pageReqVO); - } - - @Override - public void cancelTaskRecordListByTaskId(Long taskId) { - List records = otaTaskRecordMapper.selectListByTaskIdAndStatus( - taskId, IotOtaTaskRecordStatusEnum.IN_PROCESS_STATUSES); - if (CollUtil.isEmpty(records)) { - return; - } - // 批量更新 - Collection ids = convertSet(records, IotOtaTaskRecordDO::getId); - otaTaskRecordMapper.updateListByIdAndStatus(ids, IotOtaTaskRecordStatusEnum.IN_PROCESS_STATUSES, - IotOtaTaskRecordDO.builder().status(IotOtaTaskRecordStatusEnum.CANCELED.getStatus()) - .description(IotOtaTaskRecordDO.DESCRIPTION_CANCEL_BY_TASK).build()); - } - - @Override - public List getOtaTaskRecordListByDeviceIdAndStatus(Set deviceIds, Set statuses) { - return otaTaskRecordMapper.selectListByDeviceIdAndStatus(deviceIds, statuses); - } - - @Override - public List getOtaRecordListByStatus(Integer status) { - return otaTaskRecordMapper.selectListByStatus(status); - } - - @Override - public void cancelOtaTaskRecord(Long id) { - // 1. 校验记录是否存在 - IotOtaTaskRecordDO record = validateUpgradeRecordExists(id); - - // 2. 更新记录状态为取消 - int updateCount = otaTaskRecordMapper.updateByIdAndStatus(record.getId(), IotOtaTaskRecordStatusEnum.IN_PROCESS_STATUSES, - IotOtaTaskRecordDO.builder().id(id).status(IotOtaTaskRecordStatusEnum.CANCELED.getStatus()) - .description(IotOtaTaskRecordDO.DESCRIPTION_CANCEL_BY_RECORD).build()); - if (updateCount == 0) { - throw exception(OTA_TASK_RECORD_CANCEL_FAIL_STATUS_ERROR); - } - - // 3. 检查并更新任务状态 - checkAndUpdateOtaTaskStatus(record.getTaskId()); - } - - @Override - public boolean pushOtaTaskRecord(IotOtaTaskRecordDO record, IotOtaFirmwareDO fireware, IotDeviceDO device) { - try { - // 1. 推送 OTA 任务记录 - IotDeviceOtaUpgradeReqDTO params = BeanUtils.toBean(fireware, IotDeviceOtaUpgradeReqDTO.class); - IotDeviceMessage message = IotDeviceMessage.requestOf( - IotDeviceMessageMethodEnum.OTA_UPGRADE.getMethod(), params); - deviceMessageService.sendDeviceMessage(message, device); - - // 2. 更新 OTA 升级记录状态为进行中 - int updateCount = otaTaskRecordMapper.updateByIdAndStatus( - record.getId(), IotOtaTaskRecordStatusEnum.PENDING.getStatus(), - IotOtaTaskRecordDO.builder().status(IotOtaTaskRecordStatusEnum.PUSHED.getStatus()) - .description(StrUtil.format("已推送,设备消息编号({})", message.getId())).build()); - Assert.isTrue(updateCount == 1, "更新设备记录({})状态失败", record.getId()); - return true; - } catch (Exception ex) { - log.error("[pushOtaTaskRecord][推送 OTA 任务记录({}) 失败]", record.getId(), ex); - otaTaskRecordMapper.updateById(IotOtaTaskRecordDO.builder().id(record.getId()) - .description(StrUtil.format("推送失败,错误信息({})", ex.getMessage())).build()); - return false; - } - } - - private IotOtaTaskRecordDO validateUpgradeRecordExists(Long id) { - IotOtaTaskRecordDO upgradeRecord = otaTaskRecordMapper.selectById(id); - if (upgradeRecord == null) { - throw exception(OTA_TASK_RECORD_NOT_EXISTS); - } - return upgradeRecord; - } - - @Override - @Transactional(rollbackFor = Exception.class) - public void updateOtaRecordProgress(IotDeviceDO device, IotDeviceMessage message) { - // 1.1 参数解析 - IotDeviceOtaProgressReqDTO params = JsonUtils.convertObject(message.getParams(), IotDeviceOtaProgressReqDTO.class); - String version = params.getVersion(); - Assert.notBlank(version, "version 不能为空"); - Integer status = params.getStatus(); - Assert.notNull(status, "status 不能为空"); - Assert.notNull(IotOtaTaskRecordStatusEnum.of(status), "status 状态不正确"); - String description = params.getDescription(); - Integer progress = params.getProgress(); - Assert.notNull(progress, "progress 不能为空"); - Assert.isTrue(progress >= 0 && progress <= 100, "progress 必须在 0-100 之间"); - // 1.2 查询 OTA 升级记录 - List records = otaTaskRecordMapper.selectListByDeviceIdAndStatus( - device.getId(), IotOtaTaskRecordStatusEnum.IN_PROCESS_STATUSES); - if (CollUtil.isEmpty(records)) { - throw exception(OTA_TASK_RECORD_UPDATE_PROGRESS_FAIL_NO_EXISTS); - } - if (records.size() > 1) { - log.warn("[updateOtaRecordProgress][message({}) 对应升级记录过多({})]", message, records); - } - IotOtaTaskRecordDO record = CollUtil.getFirst(records); - // 1.3 查询 OTA 固件 - IotOtaFirmwareDO firmware = otaFirmwareService.getOtaFirmwareByProductIdAndVersion( - device.getProductId(), version); - if (firmware == null) { - throw exception(OTA_FIRMWARE_NOT_EXISTS); - } - - // 2. 更新 OTA 升级记录状态 - int updateCount = otaTaskRecordMapper.updateByIdAndStatus( - record.getId(), IotOtaTaskRecordStatusEnum.IN_PROCESS_STATUSES, - IotOtaTaskRecordDO.builder().status(status).description(description).progress(progress).build()); - if (updateCount == 0) { - throw exception(OTA_TASK_RECORD_UPDATE_PROGRESS_FAIL_NO_EXISTS); - } - - // 3. 如果升级成功,则更新设备固件版本 - if (IotOtaTaskRecordStatusEnum.SUCCESS.getStatus().equals(status)) { - deviceService.updateDeviceFirmware(device.getId(), firmware.getId()); - } - - // 4. 如果状态是“已结束”(非进行中),则更新任务状态 - if (!IotOtaTaskRecordStatusEnum.IN_PROCESS_STATUSES.contains(status)) { - checkAndUpdateOtaTaskStatus(record.getTaskId()); - } - } - - /** - * 检查并更新任务状态 - * 如果任务下没有进行中的记录,则将任务状态更新为已结束 - */ - private void checkAndUpdateOtaTaskStatus(Long taskId) { - // 如果还有进行中的记录,直接返回 - Long inProcessCount = otaTaskRecordMapper.selectCountByTaskIdAndStatus( - taskId, IotOtaTaskRecordStatusEnum.IN_PROCESS_STATUSES); - if (inProcessCount > 0) { - return; - } - - // 没有进行中的记录,将任务状态更新为已结束 - otaTaskService.updateOtaTaskStatusEnd(taskId); - } - -} diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/ota/IotOtaTaskService.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/ota/IotOtaTaskService.java deleted file mode 100644 index 86ef505..0000000 --- a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/ota/IotOtaTaskService.java +++ /dev/null @@ -1,55 +0,0 @@ -package cn.iocoder.yudao.module.iot.service.ota; - -import cn.iocoder.yudao.framework.common.pojo.PageResult; -import cn.iocoder.yudao.module.iot.controller.admin.ota.vo.task.IotOtaTaskCreateReqVO; -import cn.iocoder.yudao.module.iot.controller.admin.ota.vo.task.IotOtaTaskPageReqVO; -import cn.iocoder.yudao.module.iot.dal.dataobject.ota.IotOtaTaskDO; - -import javax.validation.Valid; - -/** - * IoT OTA 升级任务 Service 接口 - * - * @author Shelly Chan - */ -public interface IotOtaTaskService { - - /** - * 创建 OTA 升级任务 - * - * @param createReqVO 创建请求对象 - * @return 升级任务编号 - */ - Long createOtaTask(@Valid IotOtaTaskCreateReqVO createReqVO); - - /** - * 取消 OTA 升级任务 - * - * @param id 升级任务编号 - */ - void cancelOtaTask(Long id); - - /** - * 获取 OTA 升级任务 - * - * @param id 升级任务编号 - * @return 升级任务 - */ - IotOtaTaskDO getOtaTask(Long id); - - /** - * 分页查询 OTA 升级任务 - * - * @param pageReqVO 分页查询请求 - * @return 升级任务分页结果 - */ - PageResult getOtaTaskPage(@Valid IotOtaTaskPageReqVO pageReqVO); - - /** - * 更新 OTA 任务状态为已结束 - * - * @param id 任务编号 - */ - void updateOtaTaskStatusEnd(Long id); - -} diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/ota/IotOtaTaskServiceImpl.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/ota/IotOtaTaskServiceImpl.java deleted file mode 100644 index 2072b3e..0000000 --- a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/ota/IotOtaTaskServiceImpl.java +++ /dev/null @@ -1,167 +0,0 @@ -package cn.iocoder.yudao.module.iot.service.ota; - -import cn.hutool.core.collection.CollUtil; -import cn.hutool.core.util.ObjUtil; -import cn.iocoder.yudao.framework.common.pojo.PageResult; -import cn.iocoder.yudao.framework.common.util.object.BeanUtils; -import cn.iocoder.yudao.module.iot.controller.admin.ota.vo.task.IotOtaTaskCreateReqVO; -import cn.iocoder.yudao.module.iot.controller.admin.ota.vo.task.IotOtaTaskPageReqVO; -import cn.iocoder.yudao.module.iot.dal.dataobject.device.IotDeviceDO; -import cn.iocoder.yudao.module.iot.dal.dataobject.ota.IotOtaFirmwareDO; -import cn.iocoder.yudao.module.iot.dal.dataobject.ota.IotOtaTaskDO; -import cn.iocoder.yudao.module.iot.dal.dataobject.ota.IotOtaTaskRecordDO; -import cn.iocoder.yudao.module.iot.dal.mysql.ota.IotOtaTaskMapper; -import cn.iocoder.yudao.module.iot.enums.ota.IotOtaTaskDeviceScopeEnum; -import cn.iocoder.yudao.module.iot.enums.ota.IotOtaTaskRecordStatusEnum; -import cn.iocoder.yudao.module.iot.enums.ota.IotOtaTaskStatusEnum; -import cn.iocoder.yudao.module.iot.service.device.IotDeviceService; -import lombok.extern.slf4j.Slf4j; -import org.springframework.context.annotation.Lazy; -import org.springframework.stereotype.Service; -import org.springframework.transaction.annotation.Transactional; -import org.springframework.validation.annotation.Validated; - -import javax.annotation.Resource; -import java.util.List; -import java.util.Objects; - -import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception; -import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertSet; -import static cn.iocoder.yudao.module.iot.enums.ErrorCodeConstants.*; - -/** - * IoT OTA 升级任务 Service 实现类 - * - * @author Shelly Chan - */ -@Service -@Validated -@Slf4j -public class IotOtaTaskServiceImpl implements IotOtaTaskService { - - @Resource - private IotOtaTaskMapper otaTaskMapper; - - @Resource - private IotDeviceService deviceService; - @Resource - private IotOtaFirmwareService otaFirmwareService; - @Resource - @Lazy // 延迟,避免循环依赖报错 - private IotOtaTaskRecordService otaTaskRecordService; - - @Override - @Transactional(rollbackFor = Exception.class) - public Long createOtaTask(IotOtaTaskCreateReqVO createReqVO) { - // 1.1 校验固件信息是否存在 - IotOtaFirmwareDO firmware = otaFirmwareService.validateFirmwareExists(createReqVO.getFirmwareId()); - // 1.2 校验同一固件的升级任务名称不重复 - if (otaTaskMapper.selectByFirmwareIdAndName(firmware.getId(), createReqVO.getName()) != null) { - throw exception(OTA_TASK_CREATE_FAIL_NAME_DUPLICATE); - } - // 1.3 校验设备范围信息 - List devices = validateOtaTaskDeviceScope(createReqVO, firmware.getProductId()); - - // 2. 保存升级任务,直接转换 - IotOtaTaskDO task = BeanUtils.toBean(createReqVO, IotOtaTaskDO.class) - .setStatus(IotOtaTaskStatusEnum.IN_PROGRESS.getStatus()) - .setDeviceTotalCount(devices.size()).setDeviceSuccessCount(0); - otaTaskMapper.insert(task); - - // 3. 生成设备升级记录 - otaTaskRecordService.createOtaTaskRecordList(devices, firmware.getId(), task.getId()); - return task.getId(); - } - - @Override - @Transactional(rollbackFor = Exception.class) - public void cancelOtaTask(Long id) { - // 1.1 校验升级任务是否存在 - IotOtaTaskDO upgradeTask = validateUpgradeTaskExists(id); - // 1.2 校验升级任务是否可以取消 - if (ObjUtil.notEqual(upgradeTask.getStatus(), IotOtaTaskStatusEnum.IN_PROGRESS.getStatus())) { - throw exception(OTA_TASK_CANCEL_FAIL_STATUS_END); - } - - // 2. 更新升级任务状态为已取消 - otaTaskMapper.updateById(IotOtaTaskDO.builder() - .id(id).status(IotOtaTaskStatusEnum.CANCELED.getStatus()) - .build()); - - // 3. 更新升级记录状态为已取消 - otaTaskRecordService.cancelTaskRecordListByTaskId(id); - } - - @Override - public IotOtaTaskDO getOtaTask(Long id) { - return otaTaskMapper.selectById(id); - } - - @Override - public PageResult getOtaTaskPage(IotOtaTaskPageReqVO pageReqVO) { - return otaTaskMapper.selectPage(pageReqVO); - } - - @Override - public void updateOtaTaskStatusEnd(Long taskId) { - int updateCount = otaTaskMapper.updateByIdAndStatus(taskId, IotOtaTaskStatusEnum.IN_PROGRESS.getStatus(), - new IotOtaTaskDO().setStatus(IotOtaTaskStatusEnum.END.getStatus())); - if (updateCount == 0) { - log.warn("[updateOtaTaskStatusEnd][任务({})不存在或状态不是进行中,无法更新]", taskId); - } - } - - private List validateOtaTaskDeviceScope(IotOtaTaskCreateReqVO createReqVO, Long productId) { - // 情况一:选择设备 - if (Objects.equals(createReqVO.getDeviceScope(), IotOtaTaskDeviceScopeEnum.SELECT.getScope())) { - // 1.1 校验设备存在 - List devices = deviceService.validateDeviceListExists(createReqVO.getDeviceIds()); - for (IotDeviceDO device : devices) { - if (ObjUtil.notEqual(device.getProductId(), productId)) { - throw exception(DEVICE_NOT_EXISTS); - } - } - // 1.2 校验设备是否已经是该固件版本 - devices.forEach(device -> { - if (Objects.equals(device.getFirmwareId(), createReqVO.getFirmwareId())) { - throw exception(OTA_TASK_CREATE_FAIL_DEVICE_FIRMWARE_EXISTS, device.getDeviceName()); - } - }); - // 1.3 校验设备是否已经在升级中 - List records = otaTaskRecordService.getOtaTaskRecordListByDeviceIdAndStatus( - convertSet(devices, IotDeviceDO::getId), IotOtaTaskRecordStatusEnum.IN_PROCESS_STATUSES); - devices.forEach(device -> { - if (CollUtil.contains(records, item -> item.getDeviceId().equals(device.getId()))) { - throw exception(OTA_TASK_CREATE_FAIL_DEVICE_OTA_IN_PROCESS, device.getDeviceName()); - } - }); - return devices; - } - // 情况二:全部设备 - if (Objects.equals(createReqVO.getDeviceScope(), IotOtaTaskDeviceScopeEnum.ALL.getScope())) { - List devices = deviceService.getDeviceListByProductId(productId); - // 2.1.1 移除已经是该固件版本的设备 - devices.removeIf(device -> Objects.equals(device.getFirmwareId(), createReqVO.getFirmwareId())); - // 2.1.2 移除已经在升级中的设备 - List records = otaTaskRecordService.getOtaTaskRecordListByDeviceIdAndStatus( - convertSet(devices, IotDeviceDO::getId), IotOtaTaskRecordStatusEnum.IN_PROCESS_STATUSES); - devices.removeIf(device -> CollUtil.contains(records, - item -> item.getDeviceId().equals(device.getId()))); - // 2.2 校验是否有可升级的设备 - if (CollUtil.isEmpty(devices)) { - throw exception(OTA_TASK_CREATE_FAIL_DEVICE_EMPTY); - } - return devices; - } - throw new IllegalArgumentException("不支持的设备范围:" + createReqVO.getDeviceScope()); - } - - private IotOtaTaskDO validateUpgradeTaskExists(Long id) { - IotOtaTaskDO upgradeTask = otaTaskMapper.selectById(id); - if (Objects.isNull(upgradeTask)) { - throw exception(OTA_TASK_NOT_EXISTS); - } - return upgradeTask; - } - -} diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/product/ErpProductUnitService.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/product/ErpProductUnitService.java new file mode 100644 index 0000000..58261f0 --- /dev/null +++ b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/product/ErpProductUnitService.java @@ -0,0 +1,109 @@ +package cn.iocoder.yudao.module.iot.service.product; + +import cn.iocoder.yudao.framework.common.pojo.PageResult; +import cn.iocoder.yudao.module.iot.controller.admin.product.vo.unit.ErpProductUnitPageReqVO; +import cn.iocoder.yudao.module.iot.controller.admin.product.vo.unit.ErpProductUnitSaveReqVO; +import cn.iocoder.yudao.module.iot.dal.dataobject.product.ErpProductUnitDO; + +import javax.validation.Valid; +import java.util.Collection; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertMap; + +/** + * ERP 产品单位 Service 接口 + * + * @author 芋道源码 + */ +public interface ErpProductUnitService { + + /** + * 创建产品单位 + * + * @param createReqVO 创建信息 + * @return 编号 + */ + Long createProductUnit(@Valid ErpProductUnitSaveReqVO createReqVO); + + /** + * 更新产品单位 + * + * @param updateReqVO 更新信息 + */ + void updateProductUnit(@Valid ErpProductUnitSaveReqVO updateReqVO); + + /** + * 删除产品单位 + * + * @param id 编号 + */ + void deleteProductUnit(Long id); + + /** + * 获得产品单位 + * + * @param id 编号 + * @return 产品单位 + */ + ErpProductUnitDO getProductUnit(Long id); + + /** + * 获得产品单位分页 + * + * @param pageReqVO 分页查询 + * @return 产品单位分页 + */ + PageResult getProductUnitPage(ErpProductUnitPageReqVO pageReqVO); + + /** + * 获得指定状态的产品单位列表 + * + * @param status 状态 + * @return 产品单位列表 + */ + List getProductUnitListByStatus(Integer status); + + /** + * 获得产品单位列表 + * + * @param ids 编号数组 + * @return 产品单位列表 + */ + List getProductUnitList(Collection ids); + + /** + * 获得产品单位 Map + * + * @param ids 编号数组 + * @return 产品单位 Map + */ + default Map getProductUnitMap(Collection ids) { + if(ids.isEmpty())return new HashMap<>(); + return convertMap(getProductUnitList(ids), ErpProductUnitDO::getId); + } + + /** + * 获得产品单位列表 + * + * @return 产品单位列表 + */ + List getProductUnitList(); + + /** + * 获得产品主单位列表 + * + * @return 产品主单位列表 + */ + List getProductUnitListByFlag(); + + /** + * 根据名称获取产品单位 + * + * @param name 单位名称 + * @return 产品单位 + */ + ErpProductUnitDO getProductUnitByName(String name); +} \ No newline at end of file diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/product/ErpProductUnitServiceImpl.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/product/ErpProductUnitServiceImpl.java new file mode 100644 index 0000000..29dc29f --- /dev/null +++ b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/product/ErpProductUnitServiceImpl.java @@ -0,0 +1,140 @@ +package cn.iocoder.yudao.module.iot.service.product; + +import cn.iocoder.yudao.framework.common.exception.ErrorCode; +import cn.iocoder.yudao.framework.common.pojo.PageResult; +import cn.iocoder.yudao.framework.common.util.object.BeanUtils; + +import cn.iocoder.yudao.module.iot.controller.admin.product.vo.unit.ErpProductUnitPageReqVO; +import cn.iocoder.yudao.module.iot.controller.admin.product.vo.unit.ErpProductUnitSaveReqVO; +import cn.iocoder.yudao.module.iot.dal.dataobject.product.ErpProductUnitDO; +import cn.iocoder.yudao.module.iot.dal.mysql.product.ErpProductUnitMapper; +import cn.iocoder.yudao.module.iot.enums.product.ProductUnitEnum; +import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper; +import com.google.common.annotations.VisibleForTesting; +import org.springframework.context.annotation.Lazy; +import org.springframework.stereotype.Service; +import org.springframework.validation.annotation.Validated; + +import javax.annotation.Resource; +import java.util.Collection; +import java.util.List; + +import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception; +import static cn.iocoder.yudao.module.iot.enums.ErrorCodeConstants.*; + +/** + * ERP 产品单位 Service 实现类 + * + * @author 芋道源码 + */ +@Service +@Validated +public class ErpProductUnitServiceImpl implements ErpProductUnitService { + + @Resource + private ErpProductUnitMapper productUnitMapper; + +// @Resource +// @Lazy // 延迟加载,避免循环依赖 +// private ErpProductService productService; + + @Override + public Long createProductUnit(ErpProductUnitSaveReqVO createReqVO) { + // 1. 校验名字唯一 + validateProductUnitNameUnique(null, createReqVO.getName()); + // 2. 插入 + ErpProductUnitDO unit = BeanUtils.toBean(createReqVO, ErpProductUnitDO.class); + productUnitMapper.insert(unit); + return unit.getId(); + } + + @Override + public void updateProductUnit(ErpProductUnitSaveReqVO updateReqVO) { + if(updateReqVO.getId().equals(ProductUnitEnum.Ton.getUnitId()) || + updateReqVO.getId().equals(ProductUnitEnum.Kilogram.getUnitId()) + || updateReqVO.getId().equals(ProductUnitEnum.Gram.getUnitId()) + || updateReqVO.getId().equals(ProductUnitEnum.Each.getUnitId())){ + throw exception(new ErrorCode(40001,"内置单位不允许更改")); + } + // 1.1 校验存在 + validateProductUnitExists(updateReqVO.getId()); + // 1.2 校验名字唯一 + validateProductUnitNameUnique(updateReqVO.getId(), updateReqVO.getName()); + // 2. 更新 + ErpProductUnitDO updateObj = BeanUtils.toBean(updateReqVO, ErpProductUnitDO.class); + productUnitMapper.updateById(updateObj); + } + + @VisibleForTesting + void validateProductUnitNameUnique(Long id, String name) { + ErpProductUnitDO unit = productUnitMapper.selectByName(name); + if (unit == null) { + return; + } + // 如果 id 为空,说明不用比较是否为相同 id 的字典类型 + if (id == null) { + throw exception(PRODUCT_UNIT_NAME_DUPLICATE); + } + if (!unit.getId().equals(id)) { + throw exception(PRODUCT_UNIT_NAME_DUPLICATE); + } + } + + @Override + public void deleteProductUnit(Long id) { + if(id.equals(ProductUnitEnum.Ton.getUnitId()) || id.equals(ProductUnitEnum.Kilogram.getUnitId()) + || id.equals(ProductUnitEnum.Gram.getUnitId()) || id.equals(ProductUnitEnum.Each.getUnitId())){ + throw exception(new ErrorCode(500,"内置单位不允许更改")); + } + // 1.1 校验存在 + validateProductUnitExists(id); +// // 1.2 校验产品是否使用 +// if (productService.getProductCountByUnitId(id) > 0) { +// throw exception(PRODUCT_UNIT_EXITS_PRODUCT); +// } + // 2. 删除 + productUnitMapper.deleteById(id); + } + + private void validateProductUnitExists(Long id) { + if (productUnitMapper.selectById(id) == null) { + throw exception(PRODUCT_UNIT_NOT_EXISTS); + } + } + + @Override + public ErpProductUnitDO getProductUnit(Long id) { + return productUnitMapper.selectById(id); + } + + @Override + public PageResult getProductUnitPage(ErpProductUnitPageReqVO pageReqVO) { + return productUnitMapper.selectPage(pageReqVO); + } + + @Override + public List getProductUnitListByStatus(Integer status) { + return productUnitMapper.selectListByStatus(status); + } + + @Override + public List getProductUnitList(Collection ids) { + return productUnitMapper.selectBatchIds(ids); + } + + @Override + public List getProductUnitList() { + return productUnitMapper.selectList(); + } + + @Override + public ErpProductUnitDO getProductUnitByName(String name) { + return productUnitMapper.selectByName(name); + } + @Override + public List getProductUnitListByFlag() { + QueryWrapper wrapper = new QueryWrapper<>(); + wrapper.eq("primary_flag", "Y"); + return productUnitMapper.selectList(wrapper); + } +} \ No newline at end of file diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/product/IotProductCategoryService.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/product/IotProductCategoryService.java deleted file mode 100644 index c4189dd..0000000 --- a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/product/IotProductCategoryService.java +++ /dev/null @@ -1,103 +0,0 @@ -package cn.iocoder.yudao.module.iot.service.product; - -import cn.iocoder.yudao.framework.common.pojo.PageResult; -import cn.iocoder.yudao.module.iot.controller.admin.product.vo.category.IotProductCategoryPageReqVO; -import cn.iocoder.yudao.module.iot.controller.admin.product.vo.category.IotProductCategorySaveReqVO; -import cn.iocoder.yudao.module.iot.dal.dataobject.product.IotProductCategoryDO; - -import javax.annotation.Nullable; -import javax.validation.Valid; -import java.time.LocalDateTime; -import java.util.Collection; -import java.util.List; -import java.util.Map; - -import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertMap; - -/** - * IoT 产品分类 Service 接口 - * - * @author 芋道源码 - */ -public interface IotProductCategoryService { - - /** - * 创建产品分类 - * - * @param createReqVO 创建信息 - * @return 编号 - */ - Long createProductCategory(@Valid IotProductCategorySaveReqVO createReqVO); - - /** - * 更新产品分类 - * - * @param updateReqVO 更新信息 - */ - void updateProductCategory(@Valid IotProductCategorySaveReqVO updateReqVO); - - /** - * 删除产品分类 - * - * @param id 编号 - */ - void deleteProductCategory(Long id); - - /** - * 获得产品分类 - * - * @param id 编号 - * @return 产品分类 - */ - IotProductCategoryDO getProductCategory(Long id); - - /** - * 获得产品分类列表 - * - * @param ids 编号 - * @return 产品分类列表 - */ - List getProductCategoryList(Collection ids); - - /** - * 获得产品分类 Map - * - * @param ids 编号 - * @return 产品分类 Map - */ - default Map getProductCategoryMap(Collection ids) { - return convertMap(getProductCategoryList(ids), IotProductCategoryDO::getId); - } - - /** - * 获得产品分类分页 - * - * @param pageReqVO 分页查询 - * @return 产品分类分页 - */ - PageResult getProductCategoryPage(IotProductCategoryPageReqVO pageReqVO); - - /** - * 获得产品分类列表,根据状态 - * - * @param status 状态 - * @return 产品分类列表 - */ - List getProductCategoryListByStatus(Integer status); - - /** - * 获得产品分类数量 - * - * @param createTime 创建时间,如果为空,则统计所有分类数量 - * @return 产品分类数量 - */ - Long getProductCategoryCount(@Nullable LocalDateTime createTime); - - /** - * 获得各个品类下设备数量统计,其中 key 是产品分类名 - * - * @return 品类设备统计列表 - */ - Map getProductCategoryDeviceCountMap(); - -} \ No newline at end of file diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/product/IotProductCategoryServiceImpl.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/product/IotProductCategoryServiceImpl.java deleted file mode 100644 index 8e1682f..0000000 --- a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/product/IotProductCategoryServiceImpl.java +++ /dev/null @@ -1,123 +0,0 @@ -package cn.iocoder.yudao.module.iot.service.product; - -import cn.hutool.core.collection.CollUtil; -import cn.iocoder.yudao.framework.common.pojo.PageResult; -import cn.iocoder.yudao.framework.common.util.object.BeanUtils; -import cn.iocoder.yudao.module.iot.controller.admin.product.vo.category.IotProductCategoryPageReqVO; -import cn.iocoder.yudao.module.iot.controller.admin.product.vo.category.IotProductCategorySaveReqVO; -import cn.iocoder.yudao.module.iot.dal.dataobject.product.IotProductCategoryDO; -import cn.iocoder.yudao.module.iot.dal.dataobject.product.IotProductDO; -import cn.iocoder.yudao.module.iot.dal.mysql.product.IotProductCategoryMapper; -import cn.iocoder.yudao.module.iot.service.device.IotDeviceService; -import org.springframework.stereotype.Service; -import org.springframework.validation.annotation.Validated; - -import javax.annotation.Resource; -import java.time.LocalDateTime; -import java.util.*; - -import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception; -import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.filterList; -import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.getSumValue; -import static cn.iocoder.yudao.module.iot.enums.ErrorCodeConstants.PRODUCT_CATEGORY_NOT_EXISTS; - -/** - * IoT 产品分类 Service 实现类 - * - * @author 芋道源码 - */ -@Service -@Validated -public class IotProductCategoryServiceImpl implements IotProductCategoryService { - - @Resource - private IotProductCategoryMapper iotProductCategoryMapper; - - @Resource - private IotProductService productService; - - @Resource - private IotDeviceService deviceService; - - public Long createProductCategory(IotProductCategorySaveReqVO createReqVO) { - // 插入 - IotProductCategoryDO productCategory = BeanUtils.toBean(createReqVO, IotProductCategoryDO.class); - iotProductCategoryMapper.insert(productCategory); - // 返回 - return productCategory.getId(); - } - - @Override - public void updateProductCategory(IotProductCategorySaveReqVO updateReqVO) { - // 校验存在 - validateProductCategoryExists(updateReqVO.getId()); - // 更新 - IotProductCategoryDO updateObj = BeanUtils.toBean(updateReqVO, IotProductCategoryDO.class); - iotProductCategoryMapper.updateById(updateObj); - } - - @Override - public void deleteProductCategory(Long id) { - // 校验存在 - validateProductCategoryExists(id); - // 删除 - iotProductCategoryMapper.deleteById(id); - } - - private void validateProductCategoryExists(Long id) { - if (iotProductCategoryMapper.selectById(id) == null) { - throw exception(PRODUCT_CATEGORY_NOT_EXISTS); - } - } - - @Override - public IotProductCategoryDO getProductCategory(Long id) { - return iotProductCategoryMapper.selectById(id); - } - - @Override - public List getProductCategoryList(Collection ids) { - if (CollUtil.isEmpty(ids)) { - return CollUtil.newArrayList(); - } - return iotProductCategoryMapper.selectByIds(ids); - } - - @Override - public PageResult getProductCategoryPage(IotProductCategoryPageReqVO pageReqVO) { - return iotProductCategoryMapper.selectPage(pageReqVO); - } - - @Override - public List getProductCategoryListByStatus(Integer status) { - return iotProductCategoryMapper.selectListByStatus(status); - } - - @Override - public Long getProductCategoryCount(LocalDateTime createTime) { - return iotProductCategoryMapper.selectCountByCreateTime(createTime); - } - - @Override - public Map getProductCategoryDeviceCountMap() { - // 1. 获取所有数据 - List categories = iotProductCategoryMapper.selectList(); - List products = productService.getProductList(); - Map deviceCountMapByProductId = deviceService.getDeviceCountMapByProductId(); - - // 2. 统计每个分类下的设备数量 - Map categoryDeviceCountMap = new HashMap<>(); - for (IotProductCategoryDO category : categories) { - // 2.1 找到该分类下的所有产品 - List categoryProducts = filterList(products, - product -> Objects.equals(product.getCategoryId(), category.getId())); - // 2.2 累加设备数量 - Integer totalDeviceCount = getSumValue(categoryProducts, - product -> deviceCountMapByProductId.getOrDefault(product.getId(), 0), - Integer::sum, 0); - categoryDeviceCountMap.put(category.getName(), totalDeviceCount); - } - return categoryDeviceCountMap; - } - -} diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/product/IotProductService.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/product/IotProductService.java deleted file mode 100644 index e8af2c4..0000000 --- a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/product/IotProductService.java +++ /dev/null @@ -1,159 +0,0 @@ -package cn.iocoder.yudao.module.iot.service.product; - -import cn.iocoder.yudao.framework.common.pojo.PageResult; -import cn.iocoder.yudao.module.iot.controller.admin.product.vo.product.IotProductPageReqVO; -import cn.iocoder.yudao.module.iot.controller.admin.product.vo.product.IotProductSaveReqVO; -import cn.iocoder.yudao.module.iot.dal.dataobject.product.IotProductDO; - -import javax.annotation.Nullable; -import javax.validation.Valid; -import java.time.LocalDateTime; -import java.util.Collection; -import java.util.List; -import java.util.Map; - -import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertMap; - -/** - * IoT 产品 Service 接口 - * - * @author ahh - */ -public interface IotProductService { - - /** - * 创建产品 - * - * @param createReqVO 创建信息 - * @return 编号 - */ - Long createProduct(@Valid IotProductSaveReqVO createReqVO); - - /** - * 更新产品 - * - * @param updateReqVO 更新信息 - */ - void updateProduct(@Valid IotProductSaveReqVO updateReqVO); - - /** - * 删除产品 - * - * @param id 编号 - */ - void deleteProduct(Long id); - - /** - * 获得产品 - * - * @param id 编号 - * @return 产品 - */ - IotProductDO getProduct(Long id); - - /** - * 【缓存】获得产品 - *

- * 注意:该方法会忽略租户信息,所以调用时,需要确认会不会有跨租户访问的风险!!! - * - * @param id 编号 - * @return 产品 - */ - IotProductDO getProductFromCache(Long id); - - /** - * 根据产品 key 获得产品 - * - * @param productKey 产品 key - * @return 产品 - */ - IotProductDO getProductByProductKey(String productKey); - - /** - * 校验产品存在 - * - * @param id 编号 - * @return 产品 - */ - IotProductDO validateProductExists(Long id); - - /** - * 校验产品存在 - * - * @param productKey 产品 key - * @return 产品 - */ - IotProductDO validateProductExists(String productKey); - - /** - * 获得产品分页 - * - * @param pageReqVO 分页查询 - * @return 产品分页 - */ - PageResult getProductPage(IotProductPageReqVO pageReqVO); - - /** - * 更新产品状态 - * - * @param id 编号 - * @param status 状态 - */ - void updateProductStatus(Long id, Integer status); - - /** - * 获得所有产品 - * - * @return 产品列表 - */ - List getProductList(); - - /** - * 根据设备类型获得产品列表 - * - * @param deviceType 设备类型(可选) - * @return 产品列表 - */ - List getProductList(@Nullable Integer deviceType); - - /** - * 获得产品数量 - * - * @param createTime 创建时间,如果为空,则统计所有产品数量 - * @return 产品数量 - */ - Long getProductCount(@Nullable LocalDateTime createTime); - - /** - * 批量获得产品列表 - * - * @param ids 产品编号集合 - * @return 产品列表 - */ - List getProductList(Collection ids); - - /** - * 批量获得产品 Map - * - * @param ids 产品编号集合 - * @return 产品 Map(key: 产品编号, value: 产品) - */ - default Map getProductMap(Collection ids) { - return convertMap(getProductList(ids), IotProductDO::getId); - } - - /** - * 批量校验产品存在 - * - * @param ids 产品编号集合 - */ - void validateProductsExist(Collection ids); - - /** - * 同步产品的 TDengine 表结构 - * - * 目的:当 MySQL 和 TDengine 不同步时,强制将已发布产品的表结构同步到 TDengine 中 - */ - void syncProductPropertyTable(); - -} \ No newline at end of file diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/product/IotProductServiceImpl.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/product/IotProductServiceImpl.java deleted file mode 100644 index 70491b3..0000000 --- a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/product/IotProductServiceImpl.java +++ /dev/null @@ -1,215 +0,0 @@ -package cn.iocoder.yudao.module.iot.service.product; - -import cn.hutool.core.collection.CollUtil; -import cn.hutool.core.util.IdUtil; -import cn.iocoder.yudao.framework.common.pojo.PageResult; -import cn.iocoder.yudao.framework.common.util.object.BeanUtils; -import cn.iocoder.yudao.framework.tenant.core.aop.TenantIgnore; -import cn.iocoder.yudao.module.iot.controller.admin.product.vo.product.IotProductPageReqVO; -import cn.iocoder.yudao.module.iot.controller.admin.product.vo.product.IotProductSaveReqVO; -import cn.iocoder.yudao.module.iot.dal.dataobject.product.IotProductDO; -import cn.iocoder.yudao.module.iot.dal.mysql.product.IotProductMapper; -import cn.iocoder.yudao.module.iot.dal.redis.RedisKeyConstants; -import cn.iocoder.yudao.module.iot.enums.product.IotProductStatusEnum; -import cn.iocoder.yudao.module.iot.service.device.IotDeviceService; -import cn.iocoder.yudao.module.iot.service.device.property.IotDevicePropertyService; -import com.baomidou.dynamic.datasource.annotation.DSTransactional; -import lombok.extern.slf4j.Slf4j; -import org.springframework.cache.annotation.CacheEvict; -import org.springframework.cache.annotation.Cacheable; -import org.springframework.context.annotation.Lazy; -import org.springframework.stereotype.Service; -import org.springframework.validation.annotation.Validated; - -import javax.annotation.Resource; -import java.time.LocalDateTime; -import java.util.Collection; -import java.util.List; -import java.util.Objects; - -import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception; -import static cn.iocoder.yudao.module.iot.enums.ErrorCodeConstants.*; - -/** - * IoT 产品 Service 实现类 - * - * @author ahh - */ -@Slf4j -@Service -@Validated -public class IotProductServiceImpl implements IotProductService { - - @Resource - private IotProductMapper productMapper; - - @Resource - private IotDeviceService deviceService; - @Resource - @Lazy // 延迟加载,避免循环依赖 - private IotDevicePropertyService devicePropertyDataService; - - @Override - public Long createProduct(IotProductSaveReqVO createReqVO) { - // 1. 校验 ProductKey - if (productMapper.selectByProductKey(createReqVO.getProductKey()) != null) { - throw exception(PRODUCT_KEY_EXISTS); - } - - // 2. 插入 - IotProductDO product = BeanUtils.toBean(createReqVO, IotProductDO.class) - .setStatus(IotProductStatusEnum.UNPUBLISHED.getStatus()) - .setProductSecret(generateProductSecret()); - productMapper.insert(product); - return product.getId(); - } - - private String generateProductSecret() { - return IdUtil.fastSimpleUUID(); - } - - @Override - @CacheEvict(value = RedisKeyConstants.PRODUCT, key = "#updateReqVO.id") - public void updateProduct(IotProductSaveReqVO updateReqVO) { - updateReqVO.setProductKey(null); // 不更新产品标识 - // 1. 校验存在 - validateProductExists(updateReqVO.getId()); - - // 2. 更新 - IotProductDO updateObj = BeanUtils.toBean(updateReqVO, IotProductDO.class); - productMapper.updateById(updateObj); - } - - @Override - @CacheEvict(value = RedisKeyConstants.PRODUCT, key = "#id") - public void deleteProduct(Long id) { - // 1.1 校验存在 - IotProductDO product = validateProductExists(id); - // 1.2 发布状态不可删除 - validateProductStatus(product); - // 1.3 校验是否有设备 - if (deviceService.getDeviceCountByProductId(id) > 0) { - throw exception(PRODUCT_DELETE_FAIL_HAS_DEVICE); - } - - // 2. 删除 - productMapper.deleteById(id); - } - - @Override - public IotProductDO validateProductExists(Long id) { - IotProductDO product = productMapper.selectById(id); - if (product == null) { - throw exception(PRODUCT_NOT_EXISTS); - } - return product; - } - - @Override - public IotProductDO validateProductExists(String productKey) { - IotProductDO product = productMapper.selectByProductKey(productKey); - if (product == null) { - throw exception(PRODUCT_NOT_EXISTS); - } - return product; - } - - private void validateProductStatus(IotProductDO product) { - if (Objects.equals(product.getStatus(), IotProductStatusEnum.PUBLISHED.getStatus())) { - throw exception(PRODUCT_STATUS_NOT_DELETE); - } - } - - @Override - public IotProductDO getProduct(Long id) { - return productMapper.selectById(id); - } - - @Override - @Cacheable(value = RedisKeyConstants.PRODUCT, key = "#id", unless = "#result == null") - @TenantIgnore // 忽略租户信息 - public IotProductDO getProductFromCache(Long id) { - return productMapper.selectById(id); - } - - @Override - public IotProductDO getProductByProductKey(String productKey) { - return productMapper.selectByProductKey(productKey); - } - - @Override - public PageResult getProductPage(IotProductPageReqVO pageReqVO) { - return productMapper.selectPage(pageReqVO); - } - - @Override - @DSTransactional(rollbackFor = Exception.class) - @CacheEvict(value = RedisKeyConstants.PRODUCT, key = "#id") - public void updateProductStatus(Long id, Integer status) { - // 1. 校验存在 - validateProductExists(id); - - // 2. 更新为发布状态,需要创建产品超级表数据模型 - // TODO @芋艿:【待定 001】1)是否需要操作后,在 redis 进行缓存,实现一个“快照”的情况,类似 tl; - if (Objects.equals(status, IotProductStatusEnum.PUBLISHED.getStatus())) { - devicePropertyDataService.defineDevicePropertyData(id); - } - - // 3. 更新 - IotProductDO updateObj = IotProductDO.builder().id(id).status(status).build(); - productMapper.updateById(updateObj); - } - - @Override - public List getProductList() { - return productMapper.selectList(); - } - - @Override - public List getProductList(Integer deviceType) { - return productMapper.selectList(deviceType); - } - - @Override - public Long getProductCount(LocalDateTime createTime) { - return productMapper.selectCountByCreateTime(createTime); - } - - @Override - public List getProductList(Collection ids) { - return productMapper.selectByIds(ids); - } - - @Override - public void syncProductPropertyTable() { - // 1. 获取所有已发布的产品 - List products = productMapper.selectListByStatus( - IotProductStatusEnum.PUBLISHED.getStatus()); - log.info("[syncProductPropertyTable][开始同步,已发布产品数量({})]", products.size()); - - // 2. 遍历同步 TDengine 表结构(创建产品超级表数据模型) - int successCount = 0; - for (IotProductDO product : products) { - try { - devicePropertyDataService.defineDevicePropertyData(product.getId()); - successCount++; - log.info("[syncProductPropertyTable][产品({}/{}) 同步成功]", product.getId(), product.getName()); - } catch (Exception e) { - log.error("[syncProductPropertyTable][产品({}/{}) 同步失败]", product.getId(), product.getName(), e); - } - } - log.info("[syncProductPropertyTable][同步完成,成功({}/{})个]", successCount, products.size()); - } - - @Override - public void validateProductsExist(Collection ids) { - if (CollUtil.isEmpty(ids)) { - return; - } - List products = productMapper.selectByIds(ids); - if (products.size() != ids.size()) { - throw exception(PRODUCT_NOT_EXISTS); - } - } - -} \ No newline at end of file diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/rule/data/IotDataRuleService.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/rule/data/IotDataRuleService.java deleted file mode 100644 index 6fb5f79..0000000 --- a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/rule/data/IotDataRuleService.java +++ /dev/null @@ -1,72 +0,0 @@ -package cn.iocoder.yudao.module.iot.service.rule.data; - -import cn.iocoder.yudao.framework.common.pojo.PageResult; -import cn.iocoder.yudao.module.iot.controller.admin.rule.vo.data.rule.IotDataRulePageReqVO; -import cn.iocoder.yudao.module.iot.controller.admin.rule.vo.data.rule.IotDataRuleSaveReqVO; -import cn.iocoder.yudao.module.iot.core.mq.message.IotDeviceMessage; -import cn.iocoder.yudao.module.iot.dal.dataobject.rule.IotDataRuleDO; - -import javax.validation.Valid; -import java.util.List; - -/** - * IoT 数据流转规则 Service 接口 - * - * @author 芋道源码 - */ -public interface IotDataRuleService { - - /** - * 创建数据流转规则 - * - * @param createReqVO 创建信息 - * @return 编号 - */ - Long createDataRule(@Valid IotDataRuleSaveReqVO createReqVO); - - /** - * 更新数据流转规则 - * - * @param updateReqVO 更新信息 - */ - void updateDataRule(@Valid IotDataRuleSaveReqVO updateReqVO); - - /** - * 删除数据流转规则 - * - * @param id 编号 - */ - void deleteDataRule(Long id); - - /** - * 获得数据流转规则 - * - * @param id 编号 - * @return 数据流转规则 - */ - IotDataRuleDO getDataRule(Long id); - - /** - * 获得数据流转规则分页 - * - * @param pageReqVO 分页查询 - * @return 数据流转规则分页 - */ - PageResult getDataRulePage(IotDataRulePageReqVO pageReqVO); - - /** - * 根据数据目的编号,获得数据流转规则列表 - * - * @param sinkId 数据目的编号 - * @return 是否被使用 - */ - List getDataRuleListBySinkId(Long sinkId); - - /** - * 执行数据流转规则 - * - * @param message 消息 - */ - void executeDataRule(IotDeviceMessage message); - -} \ No newline at end of file diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/rule/data/IotDataRuleServiceImpl.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/rule/data/IotDataRuleServiceImpl.java deleted file mode 100644 index 839dcaa..0000000 --- a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/rule/data/IotDataRuleServiceImpl.java +++ /dev/null @@ -1,291 +0,0 @@ -package cn.iocoder.yudao.module.iot.service.rule.data; - -import cn.hutool.core.collection.CollUtil; -import cn.hutool.core.util.ObjUtil; -import cn.hutool.core.util.StrUtil; -import cn.iocoder.yudao.framework.common.enums.CommonStatusEnum; -import cn.iocoder.yudao.framework.common.pojo.PageResult; -import cn.iocoder.yudao.framework.common.util.object.BeanUtils; -import cn.iocoder.yudao.framework.common.util.object.ObjectUtils; -import cn.iocoder.yudao.framework.common.util.spring.SpringUtils; -import cn.iocoder.yudao.module.iot.controller.admin.rule.vo.data.rule.IotDataRulePageReqVO; -import cn.iocoder.yudao.module.iot.controller.admin.rule.vo.data.rule.IotDataRuleSaveReqVO; -import cn.iocoder.yudao.module.iot.core.mq.message.IotDeviceMessage; -import cn.iocoder.yudao.module.iot.core.util.IotDeviceMessageUtils; -import cn.iocoder.yudao.module.iot.dal.dataobject.device.IotDeviceDO; -import cn.iocoder.yudao.module.iot.dal.dataobject.rule.IotDataRuleDO; -import cn.iocoder.yudao.module.iot.dal.dataobject.rule.IotDataSinkDO; -import cn.iocoder.yudao.module.iot.dal.mysql.rule.IotDataRuleMapper; -import cn.iocoder.yudao.module.iot.dal.redis.RedisKeyConstants; -import cn.iocoder.yudao.module.iot.service.device.IotDeviceService; -import cn.iocoder.yudao.module.iot.service.product.IotProductService; -import cn.iocoder.yudao.module.iot.service.rule.data.action.IotDataRuleAction; -import cn.iocoder.yudao.module.iot.service.thingmodel.IotThingModelService; -import lombok.extern.slf4j.Slf4j; -import org.springframework.cache.annotation.CacheEvict; -import org.springframework.cache.annotation.Cacheable; -import org.springframework.stereotype.Service; -import org.springframework.validation.annotation.Validated; - -import javax.annotation.Resource; -import java.util.*; - -import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception; -import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertSet; -import static cn.iocoder.yudao.module.iot.enums.ErrorCodeConstants.DATA_RULE_NAME_EXISTS; -import static cn.iocoder.yudao.module.iot.enums.ErrorCodeConstants.DATA_RULE_NOT_EXISTS; - -/** - * IoT 数据流转规则 Service 实现类 - * - * @author 芋道源码 - */ -@Service -@Validated -@Slf4j -public class IotDataRuleServiceImpl implements IotDataRuleService { - - @Resource - private IotDataRuleMapper dataRuleMapper; - - @Resource - private IotProductService productService; - @Resource - private IotDeviceService deviceService; - @Resource - private IotThingModelService thingModelService; - @Resource - private IotDataSinkService dataSinkService; - - @Resource - private List dataRuleActions; - - @Override - @CacheEvict(value = RedisKeyConstants.DATA_RULE_LIST, allEntries = true) - public Long createDataRule(IotDataRuleSaveReqVO createReqVO) { - // 校验名称唯一 - validateDataRuleNameUnique(null, createReqVO.getName()); - // 校验数据源配置和数据目的 - validateDataRuleConfig(createReqVO); - // 新增 - IotDataRuleDO dataRule = BeanUtils.toBean(createReqVO, IotDataRuleDO.class); - dataRuleMapper.insert(dataRule); - return dataRule.getId(); - } - - @Override - @CacheEvict(value = RedisKeyConstants.DATA_RULE_LIST, allEntries = true) - public void updateDataRule(IotDataRuleSaveReqVO updateReqVO) { - // 校验存在 - validateDataRuleExists(updateReqVO.getId()); - // 校验名称唯一 - validateDataRuleNameUnique(updateReqVO.getId(), updateReqVO.getName()); - // 校验数据源配置和数据目的 - validateDataRuleConfig(updateReqVO); - - // 更新 - IotDataRuleDO updateObj = BeanUtils.toBean(updateReqVO, IotDataRuleDO.class); - dataRuleMapper.updateById(updateObj); - } - - @Override - @CacheEvict(value = RedisKeyConstants.DATA_RULE_LIST, allEntries = true) - public void deleteDataRule(Long id) { - // 校验存在 - validateDataRuleExists(id); - // 删除 - dataRuleMapper.deleteById(id); - } - - private void validateDataRuleExists(Long id) { - if (dataRuleMapper.selectById(id) == null) { - throw exception(DATA_RULE_NOT_EXISTS); - } - } - - /** - * 校验数据流转规则名称唯一性 - * - * @param id 数据流转规则编号(用于更新时排除自身) - * @param name 数据流转规则名称 - */ - private void validateDataRuleNameUnique(Long id, String name) { - if (StrUtil.isBlank(name)) { - return; - } - IotDataRuleDO dataRule = dataRuleMapper.selectByName(name); - if (dataRule == null) { - return; - } - // 如果 id 为空,说明不用比较是否为相同 id 的规则 - if (id == null) { - throw exception(DATA_RULE_NAME_EXISTS); - } - if (!dataRule.getId().equals(id)) { - throw exception(DATA_RULE_NAME_EXISTS); - } - } - - /** - * 校验数据流转规则配置 - * - * @param reqVO 数据流转规则保存请求VO - */ - private void validateDataRuleConfig(IotDataRuleSaveReqVO reqVO) { - // 1. 校验数据源配置 - validateSourceConfigs(reqVO.getSourceConfigs()); - // 2. 校验数据目的 - dataSinkService.validateDataSinksExist(reqVO.getSinkIds()); - } - - /** - * 校验数据源配置 - * - * @param sourceConfigs 数据源配置列表 - */ - private void validateSourceConfigs(List sourceConfigs) { - // 1. 校验产品 - productService.validateProductsExist( - convertSet(sourceConfigs, IotDataRuleDO.SourceConfig::getProductId)); - - // 2. 校验设备 - deviceService.validateDeviceListExists(convertSet(sourceConfigs, IotDataRuleDO.SourceConfig::getDeviceId, - config -> ObjUtil.notEqual(config.getDeviceId(), IotDeviceDO.DEVICE_ID_ALL))); - - // 3. 校验物模型存在 - validateThingModelsExist(sourceConfigs); - } - - /** - * 校验物模型存在 - * - * @param sourceConfigs 数据源配置列表 - */ - private void validateThingModelsExist(List sourceConfigs) { - Map> productIdIdentifiers = new HashMap<>(); - for (IotDataRuleDO.SourceConfig config : sourceConfigs) { - if (StrUtil.isEmpty(config.getIdentifier())) { - continue; - } - productIdIdentifiers.computeIfAbsent(config.getProductId(), - productId -> new HashSet<>()).add(config.getIdentifier()); - } - for (Map.Entry> entry : productIdIdentifiers.entrySet()) { - thingModelService.validateThingModelListExists(entry.getKey(), entry.getValue()); - } - } - - @Override - public IotDataRuleDO getDataRule(Long id) { - return dataRuleMapper.selectById(id); - } - - @Override - public PageResult getDataRulePage(IotDataRulePageReqVO pageReqVO) { - return dataRuleMapper.selectPage(pageReqVO); - } - - @Override - public List getDataRuleListBySinkId(Long sinkId) { - return dataRuleMapper.selectListBySinkId(sinkId); - } - - @Cacheable(value = RedisKeyConstants.DATA_RULE_LIST, - key = "#deviceId + '_' + #method + '_' + (#identifier ?: '')") - public List getDataRuleListByConditionFromCache(Long deviceId, String method, String identifier) { - // 1. 查询所有开启的数据流转规则 - List rules = dataRuleMapper.selectListByStatus(CommonStatusEnum.ENABLE.getStatus()); - // 2. 内存里过滤匹配的规则 - List matchedRules = new ArrayList<>(); - for (IotDataRuleDO rule : rules) { - IotDataRuleDO.SourceConfig found = CollUtil.findOne(rule.getSourceConfigs(), - config -> ObjectUtils.equalsAny(config.getDeviceId(), deviceId, IotDeviceDO.DEVICE_ID_ALL) - && Objects.equals(config.getMethod(), method) - && (StrUtil.isEmpty(config.getIdentifier()) || ObjUtil.equal(config.getIdentifier(), identifier))); - if (found != null) { - matchedRules.add(new IotDataRuleDO().setId(rule.getId()).setSinkIds(rule.getSinkIds())); - } - } - return matchedRules; - } - - @Override - public void executeDataRule(IotDeviceMessage message) { - try { - // 1. 获取匹配的数据流转规则 - Long deviceId = message.getDeviceId(); - String method = message.getMethod(); - String identifier = IotDeviceMessageUtils.getIdentifier(message); - List rules = getSelf().getDataRuleListByConditionFromCache(deviceId, method, identifier); - if (CollUtil.isEmpty(rules)) { - log.debug("[executeDataRule][设备({}) 方法({}) 标识符({}) 没有匹配的数据流转规则]", - deviceId, method, identifier); - return; - } - log.info("[executeDataRule][设备({}) 方法({}) 标识符({}) 匹配到 {} 条数据流转规则]", - deviceId, method, identifier, rules.size()); - - // 2. 遍历规则,执行数据流转 - rules.forEach(rule -> executeDataRule(message, rule)); - } catch (Exception e) { - log.error("[executeDataRule][消息({}) 执行数据流转规则异常]", message, e); - } - } - - /** - * 为指定规则的所有数据目的执行数据流转 - * - * @param message 设备消息 - * @param rule 数据流转规则 - */ - private void executeDataRule(IotDeviceMessage message, IotDataRuleDO rule) { - rule.getSinkIds().forEach(sinkId -> { - try { - // 获取数据目的配置 - IotDataSinkDO dataSink = dataSinkService.getDataSinkFromCache(sinkId); - if (dataSink == null) { - log.error("[executeDataRule][规则({}) 对应的数据目的({}) 不存在]", rule.getId(), sinkId); - return; - } - if (CommonStatusEnum.isDisable(dataSink.getStatus())) { - log.info("[executeDataRule][规则({}) 对应的数据目的({}) 状态为禁用]", rule.getId(), sinkId); - return; - } - - // 执行数据桥接操作 - executeDataRuleAction(message, dataSink); - } catch (Exception e) { - log.error("[executeDataRule][规则({}) 数据目的({}) 执行异常]", rule.getId(), sinkId, e); - } - }); - } - - /** - * 执行数据流转操作 - * - * @param message 设备消息 - * @param dataSink 数据目的 - */ - private void executeDataRuleAction(IotDeviceMessage message, IotDataSinkDO dataSink) { - dataRuleActions.forEach(action -> { - if (ObjUtil.notEqual(action.getType(), dataSink.getType())) { - return; - } - if (CommonStatusEnum.isDisable(dataSink.getStatus())) { - log.warn("[executeDataRuleAction][消息({}) 数据目的({}) 状态为禁用]", message.getId(), dataSink.getId()); - return; - } - try { - action.execute(message, dataSink); - log.info("[executeDataRuleAction][消息({}) 数据目的({}) 执行成功]", message.getId(), dataSink.getId()); - } catch (Exception e) { - log.error("[executeDataRuleAction][消息({}) 数据目的({}) 执行异常]", message.getId(), dataSink.getId(), e); - } - }); - } - - private IotDataRuleServiceImpl getSelf() { - return SpringUtils.getBean(IotDataRuleServiceImpl.class); - } - -} \ No newline at end of file diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/rule/data/IotDataSinkService.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/rule/data/IotDataSinkService.java deleted file mode 100644 index 8cd5b5d..0000000 --- a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/rule/data/IotDataSinkService.java +++ /dev/null @@ -1,80 +0,0 @@ -package cn.iocoder.yudao.module.iot.service.rule.data; - -import cn.iocoder.yudao.framework.common.pojo.PageResult; -import cn.iocoder.yudao.module.iot.controller.admin.rule.vo.data.sink.IotDataSinkPageReqVO; -import cn.iocoder.yudao.module.iot.controller.admin.rule.vo.data.sink.IotDataSinkSaveReqVO; -import cn.iocoder.yudao.module.iot.dal.dataobject.rule.IotDataSinkDO; - -import javax.validation.Valid; -import java.util.Collection; -import java.util.List; - -/** - * IoT 数据流转目的 Service 接口 - * - * @author HUIHUI - */ -public interface IotDataSinkService { - - /** - * 创建数据流转目的 - * - * @param createReqVO 创建信息 - * @return 编号 - */ - Long createDataSink(@Valid IotDataSinkSaveReqVO createReqVO); - - /** - * 更新数据流转目的 - * - * @param updateReqVO 更新信息 - */ - void updateDataSink(@Valid IotDataSinkSaveReqVO updateReqVO); - - /** - * 删除数据流转目的 - * - * @param id 编号 - */ - void deleteDataSink(Long id); - - /** - * 获得数据流转目的 - * - * @param id 编号 - * @return 数据流转目的 - */ - IotDataSinkDO getDataSink(Long id); - - /** - * 从缓存中获得数据流转目的 - * - * @param id 编号 - * @return 数据流转目的 - */ - IotDataSinkDO getDataSinkFromCache(Long id); - - /** - * 获得数据流转目的分页 - * - * @param pageReqVO 分页查询 - * @return 数据流转目的分页 - */ - PageResult getDataSinkPage(IotDataSinkPageReqVO pageReqVO); - - /** - * 获取数据流转目的列表 - * - * @param status 状态,如果为空,则不进行筛选 - * @return 数据流转目的列表 - */ - List getDataSinkListByStatus(Integer status); - - /** - * 批量校验数据目的存在 - * - * @param ids 数据目的编号集合 - */ - void validateDataSinksExist(Collection ids); - -} diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/rule/data/IotDataSinkServiceImpl.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/rule/data/IotDataSinkServiceImpl.java deleted file mode 100644 index 15d7e99..0000000 --- a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/rule/data/IotDataSinkServiceImpl.java +++ /dev/null @@ -1,136 +0,0 @@ -package cn.iocoder.yudao.module.iot.service.rule.data; - -import cn.hutool.core.collection.CollUtil; -import cn.hutool.core.util.StrUtil; -import cn.iocoder.yudao.framework.common.pojo.PageResult; -import cn.iocoder.yudao.framework.common.util.object.BeanUtils; -import cn.iocoder.yudao.module.iot.controller.admin.rule.vo.data.sink.IotDataSinkPageReqVO; -import cn.iocoder.yudao.module.iot.controller.admin.rule.vo.data.sink.IotDataSinkSaveReqVO; -import cn.iocoder.yudao.module.iot.dal.dataobject.rule.IotDataSinkDO; -import cn.iocoder.yudao.module.iot.dal.mysql.rule.IotDataSinkMapper; -import cn.iocoder.yudao.module.iot.dal.redis.RedisKeyConstants; -import org.springframework.cache.annotation.Cacheable; -import org.springframework.context.annotation.Lazy; -import org.springframework.stereotype.Service; -import org.springframework.validation.annotation.Validated; - -import javax.annotation.Resource; -import java.util.Collection; -import java.util.List; - -import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception; -import static cn.iocoder.yudao.module.iot.enums.ErrorCodeConstants.DATA_SINK_DELETE_FAIL_USED_BY_RULE; -import static cn.iocoder.yudao.module.iot.enums.ErrorCodeConstants.DATA_SINK_NAME_EXISTS; -import static cn.iocoder.yudao.module.iot.enums.ErrorCodeConstants.DATA_SINK_NOT_EXISTS; - -/** - * IoT 数据流转目的 Service 实现类 - * - * @author HUIHUI - */ -@Service -@Validated -public class IotDataSinkServiceImpl implements IotDataSinkService { - - @Resource - private IotDataSinkMapper dataSinkMapper; - - @Resource - @Lazy // 延迟,避免循环依赖报错 - private IotDataRuleService dataRuleService; - - @Override - public Long createDataSink(IotDataSinkSaveReqVO createReqVO) { - // 校验名称唯一 - validateDataSinkNameUnique(null, createReqVO.getName()); - // 新增 - IotDataSinkDO dataBridge = BeanUtils.toBean(createReqVO, IotDataSinkDO.class); - dataSinkMapper.insert(dataBridge); - return dataBridge.getId(); - } - - @Override - public void updateDataSink(IotDataSinkSaveReqVO updateReqVO) { - // 校验存在 - validateDataBridgeExists(updateReqVO.getId()); - // 校验名称唯一 - validateDataSinkNameUnique(updateReqVO.getId(), updateReqVO.getName()); - // 更新 - IotDataSinkDO updateObj = BeanUtils.toBean(updateReqVO, IotDataSinkDO.class); - dataSinkMapper.updateById(updateObj); - } - - @Override - public void deleteDataSink(Long id) { - // 校验存在 - validateDataBridgeExists(id); - // 校验是否被数据流转规则使用 - if (CollUtil.isNotEmpty(dataRuleService.getDataRuleListBySinkId(id))) { - throw exception(DATA_SINK_DELETE_FAIL_USED_BY_RULE); - } - // 删除 - dataSinkMapper.deleteById(id); - } - - private void validateDataBridgeExists(Long id) { - if (dataSinkMapper.selectById(id) == null) { - throw exception(DATA_SINK_NOT_EXISTS); - } - } - - /** - * 校验数据流转目的名称唯一性 - * - * @param id 数据流转目的编号(用于更新时排除自身) - * @param name 数据流转目的名称 - */ - private void validateDataSinkNameUnique(Long id, String name) { - if (StrUtil.isBlank(name)) { - return; - } - IotDataSinkDO dataSink = dataSinkMapper.selectByName(name); - if (dataSink == null) { - return; - } - // 如果 id 为空,说明不用比较是否为相同 id 的目的 - if (id == null) { - throw exception(DATA_SINK_NAME_EXISTS); - } - if (!dataSink.getId().equals(id)) { - throw exception(DATA_SINK_NAME_EXISTS); - } - } - - @Override - public IotDataSinkDO getDataSink(Long id) { - return dataSinkMapper.selectById(id); - } - - @Override - @Cacheable(value = RedisKeyConstants.DATA_SINK, key = "#id") - public IotDataSinkDO getDataSinkFromCache(Long id) { - return dataSinkMapper.selectById(id); - } - - @Override - public PageResult getDataSinkPage(IotDataSinkPageReqVO pageReqVO) { - return dataSinkMapper.selectPage(pageReqVO); - } - - @Override - public List getDataSinkListByStatus(Integer status) { - return dataSinkMapper.selectListByStatus(status); - } - - @Override - public void validateDataSinksExist(Collection ids) { - if (CollUtil.isEmpty(ids)) { - return; - } - List sinks = dataSinkMapper.selectByIds(ids); - if (sinks.size() != ids.size()) { - throw exception(DATA_SINK_NOT_EXISTS); - } - } - -} diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/rule/data/action/IotDataRuleAction.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/rule/data/action/IotDataRuleAction.java deleted file mode 100644 index 8e6458b..0000000 --- a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/rule/data/action/IotDataRuleAction.java +++ /dev/null @@ -1,28 +0,0 @@ -package cn.iocoder.yudao.module.iot.service.rule.data.action; - -import cn.iocoder.yudao.module.iot.core.mq.message.IotDeviceMessage; -import cn.iocoder.yudao.module.iot.dal.dataobject.rule.IotDataSinkDO; - -/** - * IoT 数据流转目的的执行器 action 接口 - * - * @author HUIHUI - */ -public interface IotDataRuleAction { - - /** - * 获取数据流转目的类型 - * - * @return 数据流转目的类型 - */ - Integer getType(); - - /** - * 执行数据流转目的操作 - * - * @param message 设备消息 - * @param dataSink 数据流转目的 - */ - void execute(IotDeviceMessage message, IotDataSinkDO dataSink); - -} diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/rule/data/action/IotDataRuleCacheableAction.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/rule/data/action/IotDataRuleCacheableAction.java deleted file mode 100644 index cc282e1..0000000 --- a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/rule/data/action/IotDataRuleCacheableAction.java +++ /dev/null @@ -1,115 +0,0 @@ -package cn.iocoder.yudao.module.iot.service.rule.data.action; - -import cn.hutool.core.lang.Assert; -import cn.hutool.core.util.ObjUtil; -import cn.iocoder.yudao.module.iot.core.mq.message.IotDeviceMessage; -import cn.iocoder.yudao.module.iot.dal.dataobject.rule.IotDataSinkDO; -import com.google.common.cache.CacheBuilder; -import com.google.common.cache.CacheLoader; -import com.google.common.cache.LoadingCache; -import com.google.common.cache.RemovalListener; -import lombok.extern.slf4j.Slf4j; - -import java.time.Duration; - -// TODO @芋艿:数据库 -// TODO @芋艿:mqtt - -/** - * 可缓存的 {@link IotDataRuleAction} 抽象实现 - * - * 该类提供了一个通用的缓存机制,用于管理各类数据桥接的生产者(Producer)实例。 - * - * 主要特点: - * - 基于Guava Cache实现高效的生产者实例缓存管理 - * - 自动处理生产者的生命周期(创建、获取、关闭) - * - 支持 30 分钟未访问自动过期清理机制 - * - 异常处理与日志记录,便于问题排查 - * - * 子类需要实现: - * - initProducer(Config) - 初始化特定类型的生产者实例 - * - closeProducer(Producer) - 关闭生产者实例并释放资源 - * - * @param 配置信息类型,用于初始化生产者 - * @param 生产者类型,负责将数据发送到目标系统 - * @author HUIHUI - */ -@Slf4j -public abstract class IotDataRuleCacheableAction implements IotDataRuleAction { - - /** - * Producer 缓存 - */ - private final LoadingCache PRODUCER_CACHE = CacheBuilder.newBuilder() - .expireAfterAccess(Duration.ofMinutes(30)) // 30 分钟未访问就提前过期 - .removalListener((RemovalListener) notification -> { - Producer producer = notification.getValue(); - try { - closeProducer(producer); - log.info("[PRODUCER_CACHE][配置({}) 对应的 producer 已关闭]", notification.getKey()); - } catch (Exception e) { - log.error("[PRODUCER_CACHE][配置({}) 对应的 producer 关闭失败]", notification.getKey(), e); - } - }) - .build(new CacheLoader() { - - @Override - public Producer load(Config config) throws Exception { - try { - Producer producer = initProducer(config); - log.info("[PRODUCER_CACHE][配置({}) 对应的 producer 已创建并启动]", config); - return producer; - } catch (Exception e) { - log.error("[PRODUCER_CACHE][配置({}) 对应的 producer 创建启动失败]", config, e); - throw e; // 抛出异常,触发缓存加载失败机制 - } - } - - }); - - /** - * 获取生产者 - * - * @param config 配置信息 - * @return 生产者对象 - */ - protected Producer getProducer(Config config) throws Exception { - return PRODUCER_CACHE.get(config); - } - - /** - * 初始化生产者 - * - * @param config 配置信息 - * @return 生产者对象 - * @throws Exception 如果初始化失败 - */ - protected abstract Producer initProducer(Config config) throws Exception; - - /** - * 关闭生产者 - * - * @param producer 生产者对象 - */ - protected abstract void closeProducer(Producer producer) throws Exception; - - @Override - @SuppressWarnings({"unchecked"}) - public void execute(IotDeviceMessage message, IotDataSinkDO dataSink) { - Assert.isTrue(ObjUtil.equal(dataSink.getType(), getType()), "类型({})不匹配", dataSink.getType()); - try { - execute(message, (Config) dataSink.getConfig()); - } catch (Exception e) { - log.error("[execute][桥梁配置 config({}) 对应的 message({}) 发送异常]", dataSink.getConfig(), message, e); - } - } - - /** - * 执行数据流转 - * - * @param message 设备消息 - * @param config 配置信息 - */ - protected abstract void execute(IotDeviceMessage message, Config config) throws Exception; - -} \ No newline at end of file diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/rule/data/action/IotHttpDataSinkAction.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/rule/data/action/IotHttpDataSinkAction.java deleted file mode 100644 index 1f6dafc..0000000 --- a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/rule/data/action/IotHttpDataSinkAction.java +++ /dev/null @@ -1,92 +0,0 @@ -package cn.iocoder.yudao.module.iot.service.rule.data.action; - -import cn.hutool.core.collection.CollUtil; -import cn.hutool.core.lang.Assert; -import cn.iocoder.yudao.framework.common.util.http.HttpUtils; -import cn.iocoder.yudao.framework.common.util.json.JsonUtils; -import cn.iocoder.yudao.module.iot.core.mq.message.IotDeviceMessage; -import cn.iocoder.yudao.module.iot.dal.dataobject.rule.IotDataSinkDO; -import cn.iocoder.yudao.module.iot.dal.dataobject.rule.config.IotDataSinkHttpConfig; -import cn.iocoder.yudao.module.iot.enums.rule.IotDataSinkTypeEnum; -import lombok.extern.slf4j.Slf4j; -import org.springframework.http.*; -import org.springframework.stereotype.Component; -import org.springframework.web.client.RestTemplate; -import org.springframework.web.util.UriComponentsBuilder; - -import javax.annotation.Resource; -import java.util.HashMap; -import java.util.Map; - -import static cn.iocoder.yudao.framework.web.core.util.WebFrameworkUtils.HEADER_TENANT_ID; - -/** - * HTTP 的 {@link IotDataRuleAction} 实现类 - * - * @author HUIHUI - */ -@Component -@Slf4j -public class IotHttpDataSinkAction implements IotDataRuleAction { - - @Resource - private RestTemplate restTemplate; - - @Override - public Integer getType() { - return IotDataSinkTypeEnum.HTTP.getType(); - } - - @Override - @SuppressWarnings("unchecked") - public void execute(IotDeviceMessage message, IotDataSinkDO dataSink) { - IotDataSinkHttpConfig config = (IotDataSinkHttpConfig) dataSink.getConfig(); - Assert.notNull(config, "配置({})不能为空", dataSink.getId()); - String url = null; - HttpMethod method = HttpMethod.valueOf(config.getMethod().toUpperCase()); - HttpEntity requestEntity = null; - ResponseEntity responseEntity = null; - try { - // 1.1 构建 Header - HttpHeaders headers = new HttpHeaders(); - if (CollUtil.isNotEmpty(config.getHeaders())) { - config.getHeaders().putAll(config.getHeaders()); - } - headers.add(HEADER_TENANT_ID, message.getTenantId().toString()); - // 1.2 构建 URL - UriComponentsBuilder uriBuilder = UriComponentsBuilder.fromUriString(config.getUrl()); - if (CollUtil.isNotEmpty(config.getQuery())) { - config.getQuery().forEach(uriBuilder::queryParam); - } - // 1.3 构建请求体 - if (method == HttpMethod.GET) { - uriBuilder.queryParam("message", HttpUtils.encodeUtf8(JsonUtils.toJsonString(message))); - url = uriBuilder.build().toUriString(); - requestEntity = new HttpEntity<>(headers); - } else { - url = uriBuilder.build().toUriString(); - Map requestBody = JsonUtils.parseObject(config.getBody(), Map.class); - if (requestBody == null) { - requestBody = new HashMap<>(); - } - requestBody.put("message", message); - headers.add(HttpHeaders.CONTENT_TYPE, MediaType.APPLICATION_JSON_UTF8_VALUE); - requestEntity = new HttpEntity<>(JsonUtils.toJsonString(requestBody), headers); - } - - // 2. 发送请求 - responseEntity = restTemplate.exchange(url, method, requestEntity, String.class); - if (responseEntity.getStatusCode().is2xxSuccessful()) { - log.info("[execute][message({}) config({}) url({}) method({}) requestEntity({}) 请求成功({})]", - message, config, url, method, requestEntity, responseEntity); - } else { - log.error("[execute][message({}) config({}) url({}) method({}) requestEntity({}) 请求失败({})]", - message, config, url, method, requestEntity, responseEntity); - } - } catch (Exception e) { - log.error("[execute][message({}) config({}) url({}) method({}) requestEntity({}) 请求异常({})]", - message, config, url, method, requestEntity, responseEntity, e); - } - } - -} \ No newline at end of file diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/rule/data/action/IotKafkaDataRuleAction.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/rule/data/action/IotKafkaDataRuleAction.java deleted file mode 100644 index 6d85798..0000000 --- a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/rule/data/action/IotKafkaDataRuleAction.java +++ /dev/null @@ -1,94 +0,0 @@ -package cn.iocoder.yudao.module.iot.service.rule.data.action; - -import cn.iocoder.yudao.framework.common.util.json.JsonUtils; -import cn.iocoder.yudao.module.iot.dal.dataobject.rule.config.IotDataSinkKafkaConfig; -import cn.iocoder.yudao.module.iot.core.mq.message.IotDeviceMessage; -import cn.iocoder.yudao.module.iot.enums.rule.IotDataSinkTypeEnum; -import lombok.extern.slf4j.Slf4j; -import org.apache.kafka.clients.producer.ProducerConfig; -import org.apache.kafka.common.serialization.StringSerializer; -import org.springframework.boot.autoconfigure.condition.ConditionalOnClass; -import org.springframework.kafka.core.DefaultKafkaProducerFactory; -import org.springframework.kafka.core.KafkaTemplate; -import org.springframework.kafka.support.SendResult; -import org.springframework.stereotype.Component; - -import java.time.Duration; -import java.util.HashMap; -import java.util.Map; -import java.util.concurrent.TimeUnit; - -/** - * Kafka 的 {@link IotDataRuleAction} 实现类 - * - * @author HUIHUI - */ -@ConditionalOnClass(name = "org.springframework.kafka.core.KafkaTemplate") -@Component -@Slf4j -public class IotKafkaDataRuleAction extends - IotDataRuleCacheableAction> { - - private static final Duration SEND_TIMEOUT = Duration.ofSeconds(10); - - @Override - public Integer getType() { - return IotDataSinkTypeEnum.KAFKA.getType(); - } - - @Override - public void execute(IotDeviceMessage message, IotDataSinkKafkaConfig config) throws Exception { - try { - // 1. 获取或创建 KafkaTemplate - KafkaTemplate kafkaTemplate = getProducer(config); - - // 2. 发送消息并等待结果 - SendResult sendResult = kafkaTemplate.send(config.getTopic(), JsonUtils.toJsonString(message)) - .get(SEND_TIMEOUT.getSeconds(), TimeUnit.SECONDS); - // 3. 处理发送结果 - if (sendResult != null && sendResult.getRecordMetadata() != null) { - log.info("[execute][message({}) config({}) 发送成功,结果: partition={}, offset={}, timestamp={}]", - message, config, - sendResult.getRecordMetadata().partition(), - sendResult.getRecordMetadata().offset(), - sendResult.getRecordMetadata().timestamp()); - } else { - log.warn("[execute][message({}) config({}) 发送结果为空]", message, config); - } - } catch (Exception e) { - log.error("[execute][message({}) config({}) 发送失败]", message, config, e); - throw e; - } - } - - @Override - protected KafkaTemplate initProducer(IotDataSinkKafkaConfig config) { - // 1.1 构建生产者配置 - Map props = new HashMap<>(); - props.put(ProducerConfig.BOOTSTRAP_SERVERS_CONFIG, config.getBootstrapServers()); - props.put(ProducerConfig.KEY_SERIALIZER_CLASS_CONFIG, StringSerializer.class); - props.put(ProducerConfig.VALUE_SERIALIZER_CLASS_CONFIG, StringSerializer.class); - // 1.2 如果配置了认证信息 - if (config.getUsername() != null && config.getPassword() != null) { - props.put("security.protocol", "SASL_PLAINTEXT"); - props.put("sasl.mechanism", "PLAIN"); - props.put("sasl.jaas.config", - "org.apache.kafka.common.security.plain.PlainLoginModule required username=\"" - + config.getUsername() + "\" password=\"" + config.getPassword() + "\";"); - } - // 1.3 如果启用 SSL - if (Boolean.TRUE.equals(config.getSsl())) { - props.put("security.protocol", "SSL"); - } - - // 2. 创建 KafkaTemplate - DefaultKafkaProducerFactory producerFactory = new DefaultKafkaProducerFactory<>(props); - return new KafkaTemplate<>(producerFactory); - } - - @Override - protected void closeProducer(KafkaTemplate producer) { - producer.destroy(); - } - -} diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/rule/data/action/IotRabbitMQDataRuleAction.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/rule/data/action/IotRabbitMQDataRuleAction.java deleted file mode 100644 index 075871a..0000000 --- a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/rule/data/action/IotRabbitMQDataRuleAction.java +++ /dev/null @@ -1,77 +0,0 @@ -package cn.iocoder.yudao.module.iot.service.rule.data.action; - -import cn.iocoder.yudao.framework.common.util.json.JsonUtils; -import cn.iocoder.yudao.module.iot.core.mq.message.IotDeviceMessage; -import cn.iocoder.yudao.module.iot.dal.dataobject.rule.config.IotDataSinkRabbitMQConfig; -import cn.iocoder.yudao.module.iot.enums.rule.IotDataSinkTypeEnum; -import com.rabbitmq.client.Channel; -import com.rabbitmq.client.Connection; -import com.rabbitmq.client.ConnectionFactory; -import lombok.extern.slf4j.Slf4j; -import org.springframework.boot.autoconfigure.condition.ConditionalOnClass; -import org.springframework.stereotype.Component; - -/** - * RabbitMQ 的 {@link IotDataRuleAction} 实现类 - * - * @author HUIHUI - */ -@ConditionalOnClass(name = "com.rabbitmq.client.Channel") -@Component -@Slf4j -public class IotRabbitMQDataRuleAction - extends IotDataRuleCacheableAction { - - @Override - public Integer getType() { - return IotDataSinkTypeEnum.RABBITMQ.getType(); - } - - @Override - public void execute(IotDeviceMessage message, IotDataSinkRabbitMQConfig config) throws Exception { - try { - // 1.1 获取或创建 Channel - Channel channel = getProducer(config); - // 1.2 声明交换机、队列和绑定关系 - channel.exchangeDeclare(config.getExchange(), "direct", true); - channel.queueDeclare(config.getQueue(), true, false, false, null); - channel.queueBind(config.getQueue(), config.getExchange(), config.getRoutingKey()); - - // 2. 发送消息 - channel.basicPublish(config.getExchange(), config.getRoutingKey(), null, - JsonUtils.toJsonByte(message)); - log.info("[execute][message({}) config({}) 发送成功]", message, config); - } catch (Exception e) { - log.error("[execute][message({}) config({}) 发送失败]", message, config, e); - throw e; - } - } - - @Override - @SuppressWarnings("resource") - protected Channel initProducer(IotDataSinkRabbitMQConfig config) throws Exception { - // 1. 创建连接工厂 - ConnectionFactory factory = new ConnectionFactory(); - factory.setHost(config.getHost()); - factory.setPort(config.getPort()); - factory.setVirtualHost(config.getVirtualHost()); - factory.setUsername(config.getUsername()); - factory.setPassword(config.getPassword()); - // 2. 创建连接 - Connection connection = factory.newConnection(); - // 3. 创建信道 - return connection.createChannel(); - } - - @Override - protected void closeProducer(Channel channel) throws Exception { - if (channel.isOpen()) { - channel.close(); - } - Connection connection = channel.getConnection(); - if (connection.isOpen()) { - connection.close(); - } - } - -} diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/rule/data/action/IotRedisRuleAction.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/rule/data/action/IotRedisRuleAction.java deleted file mode 100644 index 904240d..0000000 --- a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/rule/data/action/IotRedisRuleAction.java +++ /dev/null @@ -1,181 +0,0 @@ -package cn.iocoder.yudao.module.iot.service.rule.data.action; - -import cn.hutool.core.util.StrUtil; -import cn.iocoder.yudao.framework.common.util.json.JsonUtils; -import cn.iocoder.yudao.module.iot.core.mq.message.IotDeviceMessage; -import cn.iocoder.yudao.module.iot.dal.dataobject.rule.config.IotDataSinkRedisConfig; -import cn.iocoder.yudao.module.iot.enums.rule.IotDataSinkTypeEnum; -import cn.iocoder.yudao.module.iot.enums.rule.IotRedisDataStructureEnum; -import lombok.extern.slf4j.Slf4j; -import org.redisson.Redisson; -import org.redisson.api.RedissonClient; -import org.redisson.config.Config; -import org.redisson.config.SingleServerConfig; -import org.redisson.spring.data.connection.RedissonConnectionFactory; -import org.springframework.data.redis.connection.RedisConnectionFactory; -import org.springframework.data.redis.connection.stream.ObjectRecord; -import org.springframework.data.redis.connection.stream.StreamRecords; -import org.springframework.data.redis.core.RedisTemplate; -import org.springframework.data.redis.serializer.RedisSerializer; -import org.springframework.stereotype.Component; - -import java.util.Map; - -/** - * Redis 的 {@link IotDataRuleAction} 实现类 - * 支持多种 Redis 数据结构:Stream、Hash、List、Set、ZSet、String - * - * @author HUIHUI - */ -@Component -@Slf4j -public class IotRedisRuleAction extends - IotDataRuleCacheableAction> { - - @Override - public Integer getType() { - return IotDataSinkTypeEnum.REDIS.getType(); - } - - @Override - public void execute(IotDeviceMessage message, IotDataSinkRedisConfig config) throws Exception { - // 1. 获取 RedisTemplate - RedisTemplate redisTemplate = getProducer(config); - - // 2. 根据数据结构类型执行不同的操作 - String messageJson = JsonUtils.toJsonString(message); - IotRedisDataStructureEnum dataStructure = getDataStructureByType(config.getDataStructure()); - switch (dataStructure) { - case STREAM: - executeStream(redisTemplate, config, messageJson); - break; - case HASH: - executeHash(redisTemplate, config, message, messageJson); - break; - case LIST: - executeList(redisTemplate, config, messageJson); - break; - case SET: - executeSet(redisTemplate, config, messageJson); - break; - case ZSET: - executeZSet(redisTemplate, config, message, messageJson); - break; - case STRING: - executeString(redisTemplate, config, messageJson); - break; - default: - throw new IllegalArgumentException("不支持的 Redis 数据结构类型: " + dataStructure); - } - - log.info("[execute][消息发送成功] dataStructure: {}, config: {}", dataStructure.getName(), config); - } - - /** - * 执行 Stream 操作 - */ - private void executeStream(RedisTemplate redisTemplate, IotDataSinkRedisConfig config, String messageJson) { - ObjectRecord record = StreamRecords.newRecord() - .ofObject(messageJson).withStreamKey(config.getTopic()); - redisTemplate.opsForStream().add(record); - } - - /** - * 执行 Hash 操作 - */ - private void executeHash(RedisTemplate redisTemplate, IotDataSinkRedisConfig config, - IotDeviceMessage message, String messageJson) { - String hashField = StrUtil.isNotBlank(config.getHashField()) ? - config.getHashField() : String.valueOf(message.getDeviceId()); - redisTemplate.opsForHash().put(config.getTopic(), hashField, messageJson); - } - - /** - * 执行 List 操作 - */ - private void executeList(RedisTemplate redisTemplate, IotDataSinkRedisConfig config, String messageJson) { - redisTemplate.opsForList().rightPush(config.getTopic(), messageJson); - } - - /** - * 执行 Set 操作 - */ - private void executeSet(RedisTemplate redisTemplate, IotDataSinkRedisConfig config, String messageJson) { - redisTemplate.opsForSet().add(config.getTopic(), messageJson); - } - - /** - * 执行 ZSet 操作 - */ - private void executeZSet(RedisTemplate redisTemplate, IotDataSinkRedisConfig config, - IotDeviceMessage message, String messageJson) { - double score; - if (StrUtil.isNotBlank(config.getScoreField())) { - // 尝试从消息中获取分数字段 - try { - Map messageMap = JsonUtils.parseObject(messageJson, Map.class); - Object scoreValue = messageMap.get(config.getScoreField()); - score = scoreValue instanceof Number ? ((Number) scoreValue).doubleValue() : System.currentTimeMillis(); - } catch (Exception e) { - score = System.currentTimeMillis(); - } - } else { - // 使用当前时间戳作为分数 - score = System.currentTimeMillis(); - } - redisTemplate.opsForZSet().add(config.getTopic(), messageJson, score); - } - - /** - * 执行 String 操作 - */ - private void executeString(RedisTemplate redisTemplate, IotDataSinkRedisConfig config, String messageJson) { - redisTemplate.opsForValue().set(config.getTopic(), messageJson); - } - - @Override - protected RedisTemplate initProducer(IotDataSinkRedisConfig config) { - // 1.1 创建 Redisson 配置 - Config redissonConfig = new Config(); - SingleServerConfig serverConfig = redissonConfig.useSingleServer() - .setAddress("redis://" + config.getHost() + ":" + config.getPort()) - .setDatabase(config.getDatabase()); - // 1.2 设置密码(如果有) - if (StrUtil.isNotBlank(config.getPassword())) { - serverConfig.setPassword(config.getPassword()); - } - - // 2.1 创建 RedisTemplate 并配置 - RedissonClient redisson = Redisson.create(redissonConfig); - RedisTemplate template = new RedisTemplate<>(); - template.setConnectionFactory(new RedissonConnectionFactory(redisson)); - // 2.2 设置序列化器 - template.setKeySerializer(RedisSerializer.string()); - template.setHashKeySerializer(RedisSerializer.string()); - template.setValueSerializer(RedisSerializer.json()); - template.setHashValueSerializer(RedisSerializer.json()); - template.afterPropertiesSet(); - return template; - } - - @Override - protected void closeProducer(RedisTemplate producer) throws Exception { - RedisConnectionFactory factory = producer.getConnectionFactory(); - if (factory != null) { - ((RedissonConnectionFactory) factory).destroy(); - } - } - - /** - * 根据类型值获取数据结构枚举 - */ - private IotRedisDataStructureEnum getDataStructureByType(Integer type) { - for (IotRedisDataStructureEnum dataStructure : IotRedisDataStructureEnum.values()) { - if (dataStructure.getType().equals(type)) { - return dataStructure; - } - } - throw new IllegalArgumentException("不支持的 Redis 数据结构类型: " + type); - } - -} diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/rule/data/action/IotRocketMQDataRuleAction.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/rule/data/action/IotRocketMQDataRuleAction.java deleted file mode 100644 index d73205c..0000000 --- a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/rule/data/action/IotRocketMQDataRuleAction.java +++ /dev/null @@ -1,61 +0,0 @@ -package cn.iocoder.yudao.module.iot.service.rule.data.action; - -import cn.iocoder.yudao.framework.common.util.json.JsonUtils; -import cn.iocoder.yudao.module.iot.core.mq.message.IotDeviceMessage; -import cn.iocoder.yudao.module.iot.dal.dataobject.rule.config.IotDataSinkRocketMQConfig; -import cn.iocoder.yudao.module.iot.enums.rule.IotDataSinkTypeEnum; -import lombok.extern.slf4j.Slf4j; -import org.apache.rocketmq.client.producer.DefaultMQProducer; -import org.apache.rocketmq.client.producer.SendResult; -import org.apache.rocketmq.client.producer.SendStatus; -import org.apache.rocketmq.common.message.Message; -import org.springframework.boot.autoconfigure.condition.ConditionalOnClass; -import org.springframework.stereotype.Component; - -/** - * RocketMQ 的 {@link IotDataRuleAction} 实现类 - * - * @author HUIHUI - */ -@ConditionalOnClass(name = "org.apache.rocketmq.client.producer.DefaultMQProducer") -@Component -@Slf4j -public class IotRocketMQDataRuleAction extends - IotDataRuleCacheableAction { - - @Override - public Integer getType() { - return IotDataSinkTypeEnum.ROCKETMQ.getType(); - } - - @Override - public void execute(IotDeviceMessage message, IotDataSinkRocketMQConfig config) throws Exception { - // 1. 获取或创建 Producer - DefaultMQProducer producer = getProducer(config); - - // 2.1 创建消息对象,指定 Topic、Tag 和消息体 - Message msg = new Message(config.getTopic(), config.getTags(), JsonUtils.toJsonByte(message)); - // 2.2 发送同步消息并处理结果 - SendResult sendResult = producer.send(msg); - // 2.3 处理发送结果 - if (SendStatus.SEND_OK.equals(sendResult.getSendStatus())) { - log.info("[execute][message({}) config({}) 发送成功,结果({})]", message, config, sendResult); - } else { - log.error("[execute][message({}) config({}) 发送失败,结果({})]", message, config, sendResult); - } - } - - @Override - protected DefaultMQProducer initProducer(IotDataSinkRocketMQConfig config) throws Exception { - DefaultMQProducer producer = new DefaultMQProducer(config.getGroup()); - producer.setNamesrvAddr(config.getNameServer()); - producer.start(); - return producer; - } - - @Override - protected void closeProducer(DefaultMQProducer producer) { - producer.shutdown(); - } - -} diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/rule/data/action/IotTcpDataRuleAction.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/rule/data/action/IotTcpDataRuleAction.java deleted file mode 100644 index 74385d0..0000000 --- a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/rule/data/action/IotTcpDataRuleAction.java +++ /dev/null @@ -1,85 +0,0 @@ -package cn.iocoder.yudao.module.iot.service.rule.data.action; - -import cn.iocoder.yudao.module.iot.core.mq.message.IotDeviceMessage; -import cn.iocoder.yudao.module.iot.dal.dataobject.rule.config.IotDataSinkTcpConfig; -import cn.iocoder.yudao.module.iot.enums.rule.IotDataSinkTypeEnum; -import cn.iocoder.yudao.module.iot.service.rule.data.action.tcp.IotTcpClient; -import lombok.extern.slf4j.Slf4j; -import org.springframework.stereotype.Component; - -/** - * TCP 的 {@link IotDataRuleAction} 实现类 - *

- * 负责将设备消息发送到外部 TCP 服务器 - * 支持普通 TCP 和 SSL TCP 连接,支持 JSON 和 BINARY 数据格式 - * 使用连接池管理 TCP 连接,提高性能和资源利用率 - * - * @author HUIHUI - */ -@Component -@Slf4j -public class IotTcpDataRuleAction extends - IotDataRuleCacheableAction { - - @Override - public Integer getType() { - return IotDataSinkTypeEnum.TCP.getType(); - } - - @Override - protected IotTcpClient initProducer(IotDataSinkTcpConfig config) throws Exception { - // 1.1 参数校验 - if (config.getHost() == null || config.getHost().trim().isEmpty()) { - throw new IllegalArgumentException("TCP 服务器地址不能为空"); - } - if (config.getPort() == null || config.getPort() <= 0 || config.getPort() > 65535) { - throw new IllegalArgumentException("TCP 服务器端口无效"); - } - - // 2.1 创建 TCP 客户端 - IotTcpClient tcpClient = new IotTcpClient( - config.getHost(), - config.getPort(), - config.getConnectTimeoutMs(), - config.getReadTimeoutMs(), - config.getSsl(), - config.getDataFormat() - ); - // 2.2 连接服务器 - tcpClient.connect(); - log.info("[initProducer][TCP 客户端创建并连接成功,服务器: {}:{},SSL: {},数据格式: {}]", - config.getHost(), config.getPort(), config.getSsl(), config.getDataFormat()); - return tcpClient; - } - - @Override - protected void closeProducer(IotTcpClient producer) throws Exception { - if (producer != null) { - producer.close(); - } - } - - @Override - protected void execute(IotDeviceMessage message, IotDataSinkTcpConfig config) throws Exception { - try { - // 1.1 获取或创建 TCP 客户端 - IotTcpClient tcpClient = getProducer(config); - // 1.2 检查连接状态,如果断开则重新连接 - if (!tcpClient.isConnected()) { - log.warn("[execute][TCP 连接已断开,尝试重新连接,服务器: {}:{}]", config.getHost(), config.getPort()); - tcpClient.connect(); - } - - // 2.1 发送消息并等待结果 - tcpClient.sendMessage(message); - // 2.2 记录发送成功日志 - log.info("[execute][message({}) config({}) 发送成功,TCP 服务器: {}:{}]", - message, config, config.getHost(), config.getPort()); - } catch (Exception e) { - log.error("[execute][message({}) config({}) 发送失败,TCP 服务器: {}:{}]", - message, config, config.getHost(), config.getPort(), e); - throw e; - } - } - -} \ No newline at end of file diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/rule/data/action/IotWebSocketDataRuleAction.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/rule/data/action/IotWebSocketDataRuleAction.java deleted file mode 100644 index 7471642..0000000 --- a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/rule/data/action/IotWebSocketDataRuleAction.java +++ /dev/null @@ -1,130 +0,0 @@ -package cn.iocoder.yudao.module.iot.service.rule.data.action; - -import cn.hutool.core.util.StrUtil; -import cn.iocoder.yudao.module.iot.core.mq.message.IotDeviceMessage; -import cn.iocoder.yudao.module.iot.dal.dataobject.rule.config.IotDataSinkWebSocketConfig; -import cn.iocoder.yudao.module.iot.enums.rule.IotDataSinkTypeEnum; -import cn.iocoder.yudao.module.iot.service.rule.data.action.websocket.IotWebSocketClient; -import lombok.extern.slf4j.Slf4j; -import org.springframework.stereotype.Component; - -import java.util.concurrent.ConcurrentHashMap; -import java.util.concurrent.TimeUnit; -import java.util.concurrent.locks.ReentrantLock; - -/** - * WebSocket 的 {@link IotDataRuleAction} 实现类 - *

- * 负责将设备消息发送到外部 WebSocket 服务器 - * 支持 ws:// 和 wss:// 协议,支持 JSON 和 TEXT 数据格式 - * 使用连接池管理 WebSocket 连接,提高性能和资源利用率 - * - * @author HUIHUI - */ -@Component -@Slf4j -public class IotWebSocketDataRuleAction extends - IotDataRuleCacheableAction { - - /** - * 锁等待超时时间(毫秒) - */ - private static final long LOCK_WAIT_TIME_MS = 5000; - - /** - * 重连锁,key 为 WebSocket 服务器地址 - *

- * WebSocket 连接是与特定服务器实例绑定的,使用单机锁即可保证重连的线程安全 - */ - private final ConcurrentHashMap reconnectLocks = new ConcurrentHashMap<>(); - - @Override - public Integer getType() { - return IotDataSinkTypeEnum.WEBSOCKET.getType(); - } - - @Override - protected IotWebSocketClient initProducer(IotDataSinkWebSocketConfig config) throws Exception { - // 1. 参数校验 - if (StrUtil.isBlank(config.getServerUrl())) { - throw new IllegalArgumentException("WebSocket 服务器地址不能为空"); - } - if (!StrUtil.startWithAny(config.getServerUrl(), "ws://", "wss://")) { - throw new IllegalArgumentException("WebSocket 服务器地址必须以 ws:// 或 wss:// 开头"); - } - - // 2.1 创建 WebSocket 客户端 - IotWebSocketClient webSocketClient = new IotWebSocketClient( - config.getServerUrl(), - config.getConnectTimeoutMs(), - config.getSendTimeoutMs(), - config.getDataFormat() - ); - // 2.2 连接服务器 - webSocketClient.connect(); - log.info("[initProducer][WebSocket 客户端创建并连接成功,服务器: {},数据格式: {}]", - config.getServerUrl(), config.getDataFormat()); - return webSocketClient; - } - - @Override - protected void closeProducer(IotWebSocketClient producer) throws Exception { - if (producer != null) { - producer.close(); - } - } - - @Override - protected void execute(IotDeviceMessage message, IotDataSinkWebSocketConfig config) throws Exception { - try { - // 1.1 获取或创建 WebSocket 客户端 - IotWebSocketClient webSocketClient = getProducer(config); - - // 1.2 检查连接状态,如果断开则使用分布式锁保证重连的线程安全 - if (!webSocketClient.isConnected()) { - reconnectWithLock(webSocketClient, config); - } - - // 2.1 发送消息 - webSocketClient.sendMessage(message); - // 2.2 记录发送成功日志 - log.info("[execute][message({}) config({}) 发送成功,WebSocket 服务器: {}]", - message, config, config.getServerUrl()); - } catch (Exception e) { - log.error("[execute][message({}) config({}) 发送失败,WebSocket 服务器: {}]", - message, config, config.getServerUrl(), e); - throw e; - } - } - - // TODO @puhui999:为什么这里要加锁呀? - /** - * 使用锁进行重连,保证同一服务器地址的重连操作线程安全 - * - * @param webSocketClient WebSocket 客户端 - * @param config 配置信息 - */ - private void reconnectWithLock(IotWebSocketClient webSocketClient, IotDataSinkWebSocketConfig config) throws Exception { - ReentrantLock lock = reconnectLocks.computeIfAbsent(config.getServerUrl(), k -> new ReentrantLock()); - boolean acquired = false; - try { - acquired = lock.tryLock(LOCK_WAIT_TIME_MS, TimeUnit.MILLISECONDS); - if (!acquired) { - throw new RuntimeException("获取 WebSocket 重连锁超时,服务器: " + config.getServerUrl()); - } - // 双重检查:获取锁后再次检查连接状态,避免重复连接 - if (!webSocketClient.isConnected()) { - log.warn("[reconnectWithLock][WebSocket 连接已断开,尝试重新连接,服务器: {}]", config.getServerUrl()); - webSocketClient.connect(); - } - } catch (InterruptedException e) { - Thread.currentThread().interrupt(); - throw new RuntimeException("获取 WebSocket 重连锁被中断,服务器: " + config.getServerUrl(), e); - } finally { - if (acquired && lock.isHeldByCurrentThread()) { - lock.unlock(); - } - } - } - -} diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/rule/data/action/tcp/IotTcpClient.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/rule/data/action/tcp/IotTcpClient.java deleted file mode 100644 index faf59d3..0000000 --- a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/rule/data/action/tcp/IotTcpClient.java +++ /dev/null @@ -1,182 +0,0 @@ -package cn.iocoder.yudao.module.iot.service.rule.data.action.tcp; - -import cn.hutool.core.util.ObjUtil; -import cn.iocoder.yudao.framework.common.util.json.JsonUtils; -import cn.iocoder.yudao.module.iot.core.mq.message.IotDeviceMessage; -import cn.iocoder.yudao.module.iot.dal.dataobject.rule.config.IotDataSinkTcpConfig; -import lombok.extern.slf4j.Slf4j; - -import javax.net.ssl.SSLSocketFactory; -import java.io.BufferedReader; -import java.io.IOException; -import java.io.InputStreamReader; -import java.io.OutputStream; -import java.net.InetSocketAddress; -import java.net.Socket; -import java.nio.charset.StandardCharsets; -import java.util.concurrent.atomic.AtomicBoolean; - -/** - * IoT TCP 客户端 - *

- * 负责与外部 TCP 服务器建立连接并发送设备消息 - * 支持 JSON 和 BINARY 两种数据格式,支持 SSL 加密连接 - * - * @author HUIHUI - */ -@Slf4j -public class IotTcpClient { - - private final String host; - private final Integer port; - private final Integer connectTimeoutMs; - private final Integer readTimeoutMs; - private final Boolean ssl; - private final String dataFormat; - - private Socket socket; - private OutputStream outputStream; - private BufferedReader reader; - private final AtomicBoolean connected = new AtomicBoolean(false); - - public IotTcpClient(String host, Integer port, Integer connectTimeoutMs, Integer readTimeoutMs, - Boolean ssl, String dataFormat) { - this.host = host; - this.port = port; - this.connectTimeoutMs = connectTimeoutMs != null ? connectTimeoutMs : IotDataSinkTcpConfig.DEFAULT_CONNECT_TIMEOUT_MS; - this.readTimeoutMs = readTimeoutMs != null ? readTimeoutMs : IotDataSinkTcpConfig.DEFAULT_READ_TIMEOUT_MS; - this.ssl = ssl != null ? ssl : IotDataSinkTcpConfig.DEFAULT_SSL; - this.dataFormat = ObjUtil.defaultIfBlank(dataFormat, IotDataSinkTcpConfig.DEFAULT_DATA_FORMAT); - } - - /** - * 连接到 TCP 服务器 - */ - public void connect() throws Exception { - if (connected.get()) { - log.warn("[connect][TCP 客户端已经连接,无需重复连接]"); - return; - } - - try { - if (ssl) { - // SSL 连接 - SSLSocketFactory sslSocketFactory = (SSLSocketFactory) SSLSocketFactory.getDefault(); - socket = sslSocketFactory.createSocket(); - } else { - // 普通连接 - socket = new Socket(); - } - - // 连接服务器 - socket.connect(new InetSocketAddress(host, port), connectTimeoutMs); - socket.setSoTimeout(readTimeoutMs); - - // 获取输入输出流 - outputStream = socket.getOutputStream(); - reader = new BufferedReader(new InputStreamReader(socket.getInputStream(), StandardCharsets.UTF_8)); - - // 更新状态 - connected.set(true); - log.info("[connect][TCP 客户端连接成功,服务器地址: {}:{}]", host, port); - } catch (Exception e) { - close(); - log.error("[connect][TCP 客户端连接失败,服务器地址: {}:{}]", host, port, e); - throw e; - } - } - - /** - * 发送设备消息 - * - * @param message 设备消息 - * @throws Exception 发送异常 - */ - public void sendMessage(IotDeviceMessage message) throws Exception { - if (!connected.get()) { - throw new IllegalStateException("TCP 客户端未连接"); - } - - try { - String messageData; - if (IotDataSinkTcpConfig.DEFAULT_DATA_FORMAT.equalsIgnoreCase(dataFormat)) { - // JSON 格式 - messageData = JsonUtils.toJsonString(message); - } else { - // BINARY 格式(这里简化为字符串,实际可能需要自定义二进制协议) - messageData = message.toString(); - } - - // 发送消息 - outputStream.write(messageData.getBytes(StandardCharsets.UTF_8)); - outputStream.write('\n'); // 添加换行符作为消息分隔符 - outputStream.flush(); - log.debug("[sendMessage][发送消息成功,设备 ID: {},消息长度: {}]", - message.getDeviceId(), messageData.length()); - } catch (Exception e) { - log.error("[sendMessage][发送消息失败,设备 ID: {}]", message.getDeviceId(), e); - throw e; - } - } - - /** - * 关闭连接 - */ - public void close() { - if (!connected.get()) { - return; - } - - try { - // 关闭资源 - if (reader != null) { - try { - reader.close(); - } catch (IOException e) { - log.warn("[close][关闭输入流失败]", e); - } - } - if (outputStream != null) { - try { - outputStream.close(); - } catch (IOException e) { - log.warn("[close][关闭输出流失败]", e); - } - } - if (socket != null) { - try { - socket.close(); - } catch (IOException e) { - log.warn("[close][关闭 Socket 失败]", e); - } - } - - // 更新状态 - connected.set(false); - log.info("[close][TCP 客户端连接已关闭,服务器地址: {}:{}]", host, port); - } catch (Exception e) { - log.error("[close][关闭 TCP 客户端连接异常]", e); - } - } - - /** - * 检查连接状态 - * - * @return 是否已连接 - */ - public boolean isConnected() { - return connected.get() && socket != null && !socket.isClosed(); - } - - @Override - public String toString() { - return "IotTcpClient{" + - "host='" + host + '\'' + - ", port=" + port + - ", ssl=" + ssl + - ", dataFormat='" + dataFormat + '\'' + - ", connected=" + connected.get() + - '}'; - } - -} \ No newline at end of file diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/rule/data/action/websocket/IotWebSocketClient.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/rule/data/action/websocket/IotWebSocketClient.java deleted file mode 100644 index 8eba723..0000000 --- a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/rule/data/action/websocket/IotWebSocketClient.java +++ /dev/null @@ -1,209 +0,0 @@ -package cn.iocoder.yudao.module.iot.service.rule.data.action.websocket; - -import cn.iocoder.yudao.framework.common.util.json.JsonUtils; -import cn.iocoder.yudao.module.iot.core.mq.message.IotDeviceMessage; -import cn.iocoder.yudao.module.iot.dal.dataobject.rule.config.IotDataSinkWebSocketConfig; -import lombok.extern.slf4j.Slf4j; -import okhttp3.*; - -import java.util.concurrent.CountDownLatch; -import java.util.concurrent.TimeUnit; -import java.util.concurrent.atomic.AtomicBoolean; - -/** - * IoT WebSocket 客户端 - *

- * 负责与外部 WebSocket 服务器建立连接并发送设备消息 - * 支持 ws:// 和 wss:// 协议,支持 JSON 和 TEXT 数据格式 - * 基于 OkHttp WebSocket 实现,兼容 JDK 8+ - *

- * 注意:该类的线程安全由调用方(IotWebSocketDataRuleAction)通过分布式锁保证 - * - * @author HUIHUI - */ -@Slf4j -public class IotWebSocketClient { - - private final String serverUrl; - private final Integer connectTimeoutMs; - private final Integer sendTimeoutMs; - private final String dataFormat; - - private OkHttpClient okHttpClient; - private volatile WebSocket webSocket; - private final AtomicBoolean connected = new AtomicBoolean(false); - - public IotWebSocketClient(String serverUrl, Integer connectTimeoutMs, Integer sendTimeoutMs, String dataFormat) { - this.serverUrl = serverUrl; - this.connectTimeoutMs = connectTimeoutMs != null ? connectTimeoutMs : IotDataSinkWebSocketConfig.DEFAULT_CONNECT_TIMEOUT_MS; - this.sendTimeoutMs = sendTimeoutMs != null ? sendTimeoutMs : IotDataSinkWebSocketConfig.DEFAULT_SEND_TIMEOUT_MS; - this.dataFormat = dataFormat != null ? dataFormat : IotDataSinkWebSocketConfig.DEFAULT_DATA_FORMAT; - } - - /** - * 连接到 WebSocket 服务器 - *

- * 注意:调用方需要通过分布式锁保证并发安全 - */ - public void connect() throws Exception { - if (connected.get()) { - log.warn("[connect][WebSocket 客户端已经连接,无需重复连接]"); - return; - } - - try { - // 创建 OkHttpClient - okHttpClient = new OkHttpClient.Builder() - .connectTimeout(connectTimeoutMs, TimeUnit.MILLISECONDS) - .readTimeout(sendTimeoutMs, TimeUnit.MILLISECONDS) - .writeTimeout(sendTimeoutMs, TimeUnit.MILLISECONDS) - .build(); - - // 创建 WebSocket 请求 - Request request = new Request.Builder() - .url(serverUrl) - .build(); - - // 使用 CountDownLatch 等待连接完成 - CountDownLatch connectLatch = new CountDownLatch(1); - AtomicBoolean connectSuccess = new AtomicBoolean(false); - // 创建 WebSocket 连接 - webSocket = okHttpClient.newWebSocket(request, new IotWebSocketListener(connectLatch, connectSuccess)); - - // 等待连接完成 - boolean await = connectLatch.await(connectTimeoutMs, TimeUnit.MILLISECONDS); - if (!await || !connectSuccess.get()) { - close(); - throw new Exception("WebSocket 连接超时或失败,服务器地址: " + serverUrl); - } - log.info("[connect][WebSocket 客户端连接成功,服务器地址: {}]", serverUrl); - } catch (Exception e) { - close(); - log.error("[connect][WebSocket 客户端连接失败,服务器地址: {}]", serverUrl, e); - throw e; - } - } - - /** - * 发送设备消息 - * - * @param message 设备消息 - * @throws Exception 发送异常 - */ - public void sendMessage(IotDeviceMessage message) throws Exception { - WebSocket ws = this.webSocket; - if (!connected.get() || ws == null) { - throw new IllegalStateException("WebSocket 客户端未连接"); - } - - try { - String messageData; - if (IotDataSinkWebSocketConfig.DEFAULT_DATA_FORMAT.equalsIgnoreCase(dataFormat)) { - messageData = JsonUtils.toJsonString(message); - } else { - messageData = message.toString(); - } - - // 发送消息 - boolean success = ws.send(messageData); - if (!success) { - throw new Exception("WebSocket 发送消息失败,消息队列已满或连接已关闭"); - } - log.debug("[sendMessage][发送消息成功,设备 ID: {},消息长度: {}]", - message.getDeviceId(), messageData.length()); - } catch (Exception e) { - log.error("[sendMessage][发送消息失败,设备 ID: {}]", message.getDeviceId(), e); - throw e; - } - } - - /** - * 关闭连接 - */ - public void close() { - try { - if (webSocket != null) { - // 发送正常关闭帧,状态码 1000 表示正常关闭 - // TODO @puhui999:有没 1000 的枚举哈?在 okhttp 里 - webSocket.close(1000, "客户端主动关闭"); - webSocket = null; - } - if (okHttpClient != null) { - // 关闭连接池和调度器 - okHttpClient.dispatcher().executorService().shutdown(); - okHttpClient.connectionPool().evictAll(); - okHttpClient = null; - } - connected.set(false); - log.info("[close][WebSocket 客户端连接已关闭,服务器地址: {}]", serverUrl); - } catch (Exception e) { - log.error("[close][关闭 WebSocket 客户端连接异常]", e); - } - } - - /** - * 检查连接状态 - * - * @return 是否已连接 - */ - public boolean isConnected() { - return connected.get() && webSocket != null; - } - - @Override - public String toString() { - return "IotWebSocketClient{" + - "serverUrl='" + serverUrl + '\'' + - ", dataFormat='" + dataFormat + '\'' + - ", connected=" + connected.get() + - '}'; - } - - /** - * OkHttp WebSocket 监听器 - */ - @SuppressWarnings("NullableProblems") - private class IotWebSocketListener extends WebSocketListener { - - private final CountDownLatch connectLatch; - private final AtomicBoolean connectSuccess; - - public IotWebSocketListener(CountDownLatch connectLatch, AtomicBoolean connectSuccess) { - this.connectLatch = connectLatch; - this.connectSuccess = connectSuccess; - } - - @Override - public void onOpen(WebSocket webSocket, Response response) { - connected.set(true); - connectSuccess.set(true); - connectLatch.countDown(); - log.info("[onOpen][WebSocket 连接已打开,服务器: {}]", serverUrl); - } - - @Override - public void onMessage(WebSocket webSocket, String text) { - log.debug("[onMessage][收到消息: {}]", text); - } - - @Override - public void onClosing(WebSocket webSocket, int code, String reason) { - connected.set(false); - log.info("[onClosing][WebSocket 正在关闭,code: {}, reason: {}]", code, reason); - } - - @Override - public void onClosed(WebSocket webSocket, int code, String reason) { - connected.set(false); - log.info("[onClosed][WebSocket 已关闭,code: {}, reason: {}]", code, reason); - } - - @Override - public void onFailure(WebSocket webSocket, Throwable t, Response response) { - connected.set(false); - connectLatch.countDown(); // 确保连接失败时也释放等待 - log.error("[onFailure][WebSocket 连接失败]", t); - } - } - -} diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/rule/scene/IotSceneRuleService.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/rule/scene/IotSceneRuleService.java deleted file mode 100644 index deb31ee..0000000 --- a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/rule/scene/IotSceneRuleService.java +++ /dev/null @@ -1,110 +0,0 @@ -package cn.iocoder.yudao.module.iot.service.rule.scene; - -import cn.iocoder.yudao.framework.common.pojo.PageResult; -import cn.iocoder.yudao.module.iot.controller.admin.rule.vo.scene.IotSceneRulePageReqVO; -import cn.iocoder.yudao.module.iot.controller.admin.rule.vo.scene.IotSceneRuleSaveReqVO; -import cn.iocoder.yudao.module.iot.core.mq.message.IotDeviceMessage; -import cn.iocoder.yudao.module.iot.dal.dataobject.rule.IotSceneRuleDO; -import cn.iocoder.yudao.module.iot.enums.rule.IotSceneRuleTriggerTypeEnum; - -import javax.validation.Valid; -import java.util.Collection; -import java.util.List; - -/** - * IoT 规则场景规则 Service 接口 - * - * @author 芋道源码 - */ -public interface IotSceneRuleService { - - /** - * 创建场景联动 - * - * @param createReqVO 创建信息 - * @return 编号 - */ - Long createSceneRule(@Valid IotSceneRuleSaveReqVO createReqVO); - - /** - * 更新场景联动 - * - * @param updateReqVO 更新信息 - */ - void updateSceneRule(@Valid IotSceneRuleSaveReqVO updateReqVO); - - /** - * 更新场景联动状态 - * - * @param id 场景联动编号 - * @param status 状态 - */ - void updateSceneRuleStatus(Long id, Integer status); - - /** - * 删除场景联动 - * - * @param id 编号 - */ - void deleteSceneRule(Long id); - - /** - * 获得场景联动 - * - * @param id 编号 - * @return 场景联动 - */ - IotSceneRuleDO getSceneRule(Long id); - - /** - * 获得场景联动分页 - * - * @param pageReqVO 分页查询 - * @return 场景联动分页 - */ - PageResult getSceneRulePage(IotSceneRulePageReqVO pageReqVO); - - /** - * 校验规则场景联动规则编号们是否存在。如下情况,视为无效: - * 1. 规则场景联动规则编号不存在 - * - * @param ids 场景联动规则编号数组 - */ - void validateSceneRuleList(Collection ids); - - /** - * 获得指定状态的场景联动列表 - * - * @param status 状态 - * @return 场景联动列表 - */ - List getSceneRuleListByStatus(Integer status); - - /** - * 【缓存】获得指定设备的场景列表 - * - * @param productId 产品 ID - * @param deviceId 设备 ID - * @return 场景列表 - */ - List getSceneRuleListByProductIdAndDeviceIdFromCache(Long productId, Long deviceId); - - /** - * 基于 {@link IotSceneRuleTriggerTypeEnum} 场景,执行规则场景 - * 1. {@link IotSceneRuleTriggerTypeEnum#DEVICE_STATE_UPDATE} - * 2. {@link IotSceneRuleTriggerTypeEnum#DEVICE_PROPERTY_POST} - * {@link IotSceneRuleTriggerTypeEnum#DEVICE_EVENT_POST} - * 3. {@link IotSceneRuleTriggerTypeEnum#DEVICE_EVENT_POST} - * {@link IotSceneRuleTriggerTypeEnum#DEVICE_SERVICE_INVOKE} - * @param message 消息 - */ - void executeSceneRuleByDevice(IotDeviceMessage message); - - /** - * 基于 {@link IotSceneRuleTriggerTypeEnum#TIMER} 场景,执行规则场景 - * - * @param id 场景联动规则编号 - */ - void executeSceneRuleByTimer(Long id); - -} diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/rule/scene/IotSceneRuleServiceImpl.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/rule/scene/IotSceneRuleServiceImpl.java deleted file mode 100644 index 4d7decc..0000000 --- a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/rule/scene/IotSceneRuleServiceImpl.java +++ /dev/null @@ -1,502 +0,0 @@ -package cn.iocoder.yudao.module.iot.service.rule.scene; - -import cn.hutool.core.collection.CollUtil; -import cn.hutool.core.collection.ListUtil; -import cn.hutool.core.util.ObjUtil; -import cn.hutool.extra.spring.SpringUtil; -import cn.iocoder.yudao.framework.common.enums.CommonStatusEnum; -import cn.iocoder.yudao.framework.common.pojo.PageResult; -import cn.iocoder.yudao.framework.common.util.object.BeanUtils; -import cn.iocoder.yudao.framework.tenant.core.aop.TenantIgnore; -import cn.iocoder.yudao.framework.tenant.core.util.TenantUtils; -import cn.iocoder.yudao.module.iot.controller.admin.rule.vo.scene.IotSceneRulePageReqVO; -import cn.iocoder.yudao.module.iot.controller.admin.rule.vo.scene.IotSceneRuleSaveReqVO; -import cn.iocoder.yudao.module.iot.core.mq.message.IotDeviceMessage; -import cn.iocoder.yudao.module.iot.dal.dataobject.device.IotDeviceDO; -import cn.iocoder.yudao.module.iot.dal.dataobject.product.IotProductDO; -import cn.iocoder.yudao.module.iot.dal.dataobject.rule.IotSceneRuleDO; -import cn.iocoder.yudao.module.iot.dal.mysql.rule.IotSceneRuleMapper; -import cn.iocoder.yudao.module.iot.dal.redis.RedisKeyConstants; -import cn.iocoder.yudao.module.iot.enums.rule.IotSceneRuleTriggerTypeEnum; -import cn.iocoder.yudao.module.iot.service.device.IotDeviceService; -import cn.iocoder.yudao.module.iot.service.product.IotProductService; -import cn.iocoder.yudao.module.iot.service.rule.scene.action.IotSceneRuleAction; -import cn.iocoder.yudao.module.iot.service.rule.scene.matcher.IotSceneRuleMatcherManager; -import cn.iocoder.yudao.module.iot.service.rule.scene.timer.IotSceneRuleTimerHandler; -import cn.iocoder.yudao.module.iot.service.rule.scene.timer.IotTimerConditionEvaluator; -import lombok.extern.slf4j.Slf4j; -import org.springframework.cache.annotation.CacheEvict; -import org.springframework.cache.annotation.Cacheable; -import org.springframework.stereotype.Service; -import org.springframework.validation.annotation.Validated; - -import javax.annotation.Resource; -import java.time.LocalDateTime; -import java.util.Collection; -import java.util.List; - -import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception; -import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.filterList; -import static cn.iocoder.yudao.module.iot.enums.ErrorCodeConstants.RULE_SCENE_NOT_EXISTS; - -/** - * IoT 规则场景 Service 实现类 - * - * @author 芋道源码 - */ -@Service -@Validated -@Slf4j -public class IotSceneRuleServiceImpl implements IotSceneRuleService { - - @Resource - private IotSceneRuleMapper sceneRuleMapper; - - @Resource - private IotProductService productService; - @Resource - private IotDeviceService deviceService; - - @Resource - private IotSceneRuleMatcherManager sceneRuleMatcherManager; - @Resource - private List sceneRuleActions; - @Resource - private IotSceneRuleTimerHandler timerHandler; - @Resource - private IotTimerConditionEvaluator timerConditionEvaluator; - - @Override - @CacheEvict(value = RedisKeyConstants.SCENE_RULE_LIST, allEntries = true) - public Long createSceneRule(IotSceneRuleSaveReqVO createReqVO) { - IotSceneRuleDO sceneRule = BeanUtils.toBean(createReqVO, IotSceneRuleDO.class); - sceneRuleMapper.insert(sceneRule); - - // 注册定时触发器 - timerHandler.registerTimerTriggers(sceneRule); - - return sceneRule.getId(); - } - - @Override - @CacheEvict(value = RedisKeyConstants.SCENE_RULE_LIST, allEntries = true) - public void updateSceneRule(IotSceneRuleSaveReqVO updateReqVO) { - // 校验存在 - validateSceneRuleExists(updateReqVO.getId()); - // 更新 - IotSceneRuleDO updateObj = BeanUtils.toBean(updateReqVO, IotSceneRuleDO.class); - sceneRuleMapper.updateById(updateObj); - - // 更新定时触发器 - timerHandler.updateTimerTriggers(updateObj); - } - - @Override - @CacheEvict(value = RedisKeyConstants.SCENE_RULE_LIST, allEntries = true) - public void updateSceneRuleStatus(Long id, Integer status) { - // 1. 校验存在 - validateSceneRuleExists(id); - - // 2. 更新状态 - IotSceneRuleDO updateObj = new IotSceneRuleDO().setId(id).setStatus(status); - sceneRuleMapper.updateById(updateObj); - - // 3. 根据状态管理定时触发器 - if (CommonStatusEnum.isEnable(status)) { - // 启用时,获取完整的场景规则信息并注册定时触发器 - IotSceneRuleDO sceneRule = sceneRuleMapper.selectById(id); - if (sceneRule != null) { - timerHandler.registerTimerTriggers(sceneRule); - } - } else { - // 禁用时,暂停定时触发器 - timerHandler.pauseTimerTriggers(id); - } - } - - @Override - @CacheEvict(value = RedisKeyConstants.SCENE_RULE_LIST, allEntries = true) - public void deleteSceneRule(Long id) { - // 1. 校验存在 - validateSceneRuleExists(id); - - // 2. 删除 - sceneRuleMapper.deleteById(id); - - // 3. 删除定时触发器 - timerHandler.unregisterTimerTriggers(id); - } - - private void validateSceneRuleExists(Long id) { - if (sceneRuleMapper.selectById(id) == null) { - throw exception(RULE_SCENE_NOT_EXISTS); - } - } - - @Override - public IotSceneRuleDO getSceneRule(Long id) { - return sceneRuleMapper.selectById(id); - } - - @Override - public PageResult getSceneRulePage(IotSceneRulePageReqVO pageReqVO) { - return sceneRuleMapper.selectPage(pageReqVO); - } - - @Override - public void validateSceneRuleList(Collection ids) { - if (CollUtil.isEmpty(ids)) { - return; - } - // 批量查询存在的规则场景 - List existingScenes = sceneRuleMapper.selectByIds(ids); - if (existingScenes.size() != ids.size()) { - throw exception(RULE_SCENE_NOT_EXISTS); - } - } - - @Override - public List getSceneRuleListByStatus(Integer status) { - return sceneRuleMapper.selectListByStatus(status); - } - - @Override - @Cacheable(value = RedisKeyConstants.SCENE_RULE_LIST, key = "#productId + '_' + #deviceId ") - @TenantIgnore // 忽略租户隔离:因为 IotSceneRuleMessageHandler 调用时,一般未传递租户,所以需要忽略 - public List getSceneRuleListByProductIdAndDeviceIdFromCache(Long productId, Long deviceId) { - // 1. 查询启用状态的规则场景 - List enabledList = sceneRuleMapper.selectList(IotSceneRuleDO::getStatus, CommonStatusEnum.ENABLE.getStatus()); - - // 2. 根据 productKey 和 deviceName 进行匹配 - return filterList(enabledList, sceneRule -> { - if (CollUtil.isEmpty(sceneRule.getTriggers())) { - return false; - } - - for (IotSceneRuleDO.Trigger trigger : sceneRule.getTriggers()) { - // 检查触发器是否匹配指定的产品和设备 - try { - // 检查产品是否匹配 - if (trigger.getProductId() == null || trigger.getDeviceId() == null) { - return false; - } - // 检查是否是全部设备的特殊标识 - if (IotDeviceDO.DEVICE_ID_ALL.equals(trigger.getDeviceId())) { - return true; - } - // 检查具体设备 ID 是否匹配 - return ObjUtil.equal(productId, trigger.getProductId()) && ObjUtil.equal(deviceId, trigger.getDeviceId()); - } catch (Exception e) { - log.warn("[getSceneRuleListByProductIdAndDeviceIdFromCache][产品({}) 设备({}) 匹配触发器异常]", - productId, deviceId, e); - return false; - } - } - return false; - }); - } - - @Override - public void executeSceneRuleByDevice(IotDeviceMessage message) { - // 1.1 这里的 tenantId,通过设备获取; - IotDeviceDO device = deviceService.getDeviceFromCache(message.getDeviceId()); - TenantUtils.execute(device.getTenantId(), () -> { - // 1.2 获得设备匹配的规则场景 - List sceneRules = getMatchedSceneRuleListByMessage(message); - if (CollUtil.isEmpty(sceneRules)) { - return; - } - - // 2. 执行规则场景 - executeSceneRuleAction(message, sceneRules); - }); - } - - @Override - public void executeSceneRuleByTimer(Long id) { - // 1.1 获得规则场景 - IotSceneRuleDO scene = TenantUtils.executeIgnore(() -> sceneRuleMapper.selectById(id)); - if (scene == null) { - log.error("[executeSceneRuleByTimer][规则场景({}) 不存在]", id); - return; - } - if (CommonStatusEnum.isDisable(scene.getStatus())) { - log.info("[executeSceneRuleByTimer][规则场景({}) 已被禁用]", id); - return; - } - // 1.2 判断是否有定时触发器,避免脏数据 - IotSceneRuleDO.Trigger timerTrigger = CollUtil.findOne(scene.getTriggers(), - trigger -> ObjUtil.equals(trigger.getType(), IotSceneRuleTriggerTypeEnum.TIMER.getType())); - if (timerTrigger == null) { - log.error("[executeSceneRuleByTimer][规则场景({}) 不存在定时触发器]", scene); - return; - } - - // 2. 评估条件组(新增逻辑) - log.info("[executeSceneRuleByTimer][规则场景({}) 开始评估条件组]", id); - if (!evaluateTimerConditionGroups(scene, timerTrigger)) { - log.info("[executeSceneRuleByTimer][规则场景({}) 条件组不满足,跳过执行]", id); - return; - } - log.info("[executeSceneRuleByTimer][规则场景({}) 条件组评估通过,准备执行动作]", id); - - // 3. 执行规则场景 - TenantUtils.execute(scene.getTenantId(), - () -> executeSceneRuleAction(null, ListUtil.toList(scene))); - } - - /** - * 评估定时触发器的条件组 - * - * @param scene 场景规则 - * @param trigger 定时触发器 - * @return 是否满足条件 - */ - private boolean evaluateTimerConditionGroups(IotSceneRuleDO scene, IotSceneRuleDO.Trigger trigger) { - // 1. 如果没有条件组,直接返回 true(直接执行动作) - if (CollUtil.isEmpty(trigger.getConditionGroups())) { - log.debug("[evaluateTimerConditionGroups][规则场景({}) 无条件组配置,直接执行]", scene.getId()); - return true; - } - - // 2. 条件组之间是 OR 关系,任一条件组满足即可 - for (List conditionGroup : trigger.getConditionGroups()) { - if (evaluateSingleConditionGroup(scene, conditionGroup)) { - log.debug("[evaluateTimerConditionGroups][规则场景({}) 条件组匹配成功]", scene.getId()); - return true; - } - } - - // 3. 所有条件组都不满足 - log.debug("[evaluateTimerConditionGroups][规则场景({}) 所有条件组都不满足]", scene.getId()); - return false; - } - - /** - * 评估单个条件组 - * - * @param scene 场景规则 - * @param conditionGroup 条件组 - * @return 是否满足条件 - */ - private boolean evaluateSingleConditionGroup(IotSceneRuleDO scene, - List conditionGroup) { - // 1. 空条件组视为满足 - if (CollUtil.isEmpty(conditionGroup)) { - return true; - } - - // 2. 条件之间是 AND 关系,所有条件都必须满足 - for (IotSceneRuleDO.TriggerCondition condition : conditionGroup) { - if (!evaluateTimerCondition(scene, condition)) { - log.debug("[evaluateSingleConditionGroup][规则场景({}) 条件({}) 不满足]", - scene.getId(), condition); - return false; - } - } - - return true; - } - - /** - * 评估单个条件(定时触发器专用) - * - * @param scene 场景规则 - * @param condition 条件 - * @return 是否满足条件 - */ - private boolean evaluateTimerCondition(IotSceneRuleDO scene, IotSceneRuleDO.TriggerCondition condition) { - try { - boolean result = timerConditionEvaluator.evaluate(condition); - log.debug("[evaluateTimerCondition][规则场景({}) 条件类型({}) 评估结果: {}]", - scene.getId(), condition.getType(), result); - return result; - } catch (Exception e) { - log.error("[evaluateTimerCondition][规则场景({}) 条件评估异常]", scene.getId(), e); - return false; - } - } - - /** - * 基于消息,获得匹配的规则场景列表 - * - * @param message 设备消息 - * @return 规则场景列表 - */ - private List getMatchedSceneRuleListByMessage(IotDeviceMessage message) { - // 1.1 通过 deviceId 获取设备信息 - IotDeviceDO device = deviceService.getDeviceFromCache(message.getDeviceId()); - if (device == null) { - log.warn("[getMatchedSceneRuleListByMessage][设备({}) 不存在]", message.getDeviceId()); - return ListUtil.of(); - } - // 1.2 通过 productId 获取产品信息 - IotProductDO product = productService.getProductFromCache(device.getProductId()); - if (product == null) { - log.warn("[getMatchedSceneRuleListByMessage][产品({}) 不存在]", device.getProductId()); - return ListUtil.of(); - } - // 1.3 获取匹配的规则场景 - List sceneRules = getSelf().getSceneRuleListByProductIdAndDeviceIdFromCache( - product.getId(), device.getId()); - if (CollUtil.isEmpty(sceneRules)) { - return sceneRules; - } - - // 2. 使用重构后的触发器匹配逻辑 - return filterList(sceneRules, sceneRule -> matchSceneRuleTriggers(message, sceneRule)); - } - - /** - * 匹配场景规则的所有触发器 - * - * @param message 设备消息 - * @param sceneRule 场景规则 - * @return 是否匹配 - */ - private boolean matchSceneRuleTriggers(IotDeviceMessage message, IotSceneRuleDO sceneRule) { - if (CollUtil.isEmpty(sceneRule.getTriggers())) { - log.debug("[matchSceneRuleTriggers][规则场景({}) 没有配置触发器]", sceneRule.getId()); - return false; - } - - for (IotSceneRuleDO.Trigger trigger : sceneRule.getTriggers()) { - if (matchSingleTrigger(message, trigger, sceneRule)) { - log.info("[matchSceneRuleTriggers][消息({}) 匹配到规则场景编号({}) 的触发器({})]", - message.getRequestId(), sceneRule.getId(), trigger.getType()); - return true; - } - } - return false; - } - - /** - * 匹配单个触发器 - * - * @param message 设备消息 - * @param trigger 触发器 - * @param sceneRule 场景规则(用于日志) - * @return 是否匹配 - */ - private boolean matchSingleTrigger(IotDeviceMessage message, IotSceneRuleDO.Trigger trigger, IotSceneRuleDO sceneRule) { - try { - return sceneRuleMatcherManager.isMatched(message, trigger) && isTriggerConditionGroupsMatched(message, trigger, sceneRule); - } catch (Exception e) { - log.error("[matchSingleTrigger][触发器匹配异常] sceneRuleId: {}, triggerType: {}, message: {}", - sceneRule.getId(), trigger.getType(), message, e); - return false; - } - } - - /** - * 检查触发器的条件分组是否匹配 - * - * @param message 设备消息 - * @param trigger 触发器 - * @param sceneRule 场景规则(用于日志) - * @return 是否匹配 - */ - private boolean isTriggerConditionGroupsMatched(IotDeviceMessage message, - IotSceneRuleDO.Trigger trigger, - IotSceneRuleDO sceneRule) { - // 1. 如果没有条件分组,则认为匹配成功(只依赖基础触发器匹配) - if (CollUtil.isEmpty(trigger.getConditionGroups())) { - return true; - } - - // 2. 检查条件分组:分组与分组之间是"或"的关系,条件与条件之间是"且"的关系 - for (List conditionGroup : trigger.getConditionGroups()) { - if (CollUtil.isEmpty(conditionGroup)) { - continue; - } - // 检查当前分组中的所有条件是否都匹配(且关系) - boolean allConditionsMatched = true; - for (IotSceneRuleDO.TriggerCondition condition : conditionGroup) { - if (!isTriggerConditionMatched(message, condition, sceneRule, trigger)) { - allConditionsMatched = false; - break; - } - } - // 如果当前分组的所有条件都匹配,则整个触发器匹配成功 - if (allConditionsMatched) { - return true; - } - } - - // 3. 所有分组都不匹配 - return false; - } - - /** - * 基于消息,判断触发器的子条件是否匹配 - * - * @param message 设备消息 - * @param condition 触发条件 - * @param sceneRule 规则场景(用于日志,无其它作用) - * @param trigger 触发器(用于日志,无其它作用) - * @return 是否匹配 - */ - private boolean isTriggerConditionMatched(IotDeviceMessage message, IotSceneRuleDO.TriggerCondition condition, - IotSceneRuleDO sceneRule, IotSceneRuleDO.Trigger trigger) { - try { - return sceneRuleMatcherManager.isConditionMatched(message, condition); - } catch (Exception e) { - log.error("[isTriggerConditionMatched][规则场景编号({}) 的触发器({}) 条件匹配异常]", - sceneRule.getId(), trigger, e); - return false; - } - } - - /** - * 执行规则场景的动作 - * - * @param message 设备消息 - * @param sceneRules 规则场景列表 - */ - private void executeSceneRuleAction(IotDeviceMessage message, List sceneRules) { - // 1. 遍历规则场景 - sceneRules.forEach(sceneRule -> { - // 2. 遍历规则场景的动作 - sceneRule.getActions().forEach(actionConfig -> { - // 2.1 获取对应的动作 Action 数组 - List actions = filterList(sceneRuleActions, - action -> action.getType().getType().equals(actionConfig.getType())); - if (CollUtil.isEmpty(actions)) { - return; - } - // 2.2 执行动作 - actions.forEach(action -> { - try { - action.execute(message, sceneRule, actionConfig); - log.info("[executeSceneRuleAction][消息({}) 规则场景编号({}) 的执行动作({}) 成功]", - message, sceneRule.getId(), actionConfig); - } catch (Exception e) { - log.error("[executeSceneRuleAction][消息({}) 规则场景编号({}) 的执行动作({}) 执行异常]", - message, sceneRule.getId(), actionConfig, e); - } - }); - }); - - // 3. 更新最后触发时间 - updateLastTriggerTime(sceneRule.getId()); - }); - } - - /** - * 更新规则场景的最后触发时间 - * - * @param id 规则场景编号 - */ - private void updateLastTriggerTime(Long id) { - try { - sceneRuleMapper.updateById(new IotSceneRuleDO().setId(id).setLastTriggerTime(LocalDateTime.now())); - } catch (Exception e) { - log.error("[updateLastTriggerTime][规则场景编号({}) 更新最后触发时间异常]", id, e); - } - } - - private IotSceneRuleServiceImpl getSelf() { - return SpringUtil.getBean(IotSceneRuleServiceImpl.class); - } - -} diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/rule/scene/IotSceneRuleTimeHelper.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/rule/scene/IotSceneRuleTimeHelper.java deleted file mode 100644 index df1ac23..0000000 --- a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/rule/scene/IotSceneRuleTimeHelper.java +++ /dev/null @@ -1,219 +0,0 @@ -package cn.iocoder.yudao.module.iot.service.rule.scene; - -import cn.hutool.core.lang.Assert; -import cn.hutool.core.text.CharPool; -import cn.hutool.core.util.StrUtil; -import cn.iocoder.yudao.module.iot.enums.rule.IotSceneRuleConditionOperatorEnum; -import cn.iocoder.yudao.module.iot.service.rule.scene.matcher.condition.IotCurrentTimeConditionMatcher; -import cn.iocoder.yudao.module.iot.service.rule.scene.timer.IotTimerConditionEvaluator; -import lombok.extern.slf4j.Slf4j; - -import java.time.LocalDateTime; -import java.time.LocalTime; -import java.time.ZoneId; -import java.time.format.DateTimeFormatter; -import java.util.List; - -/** - * IoT 场景规则时间匹配工具类 - *

- * 提供时间条件匹配的通用方法,供 {@link IotCurrentTimeConditionMatcher} 和 {@link IotTimerConditionEvaluator} 共同使用。 - * - * @author HUIHUI - */ -@Slf4j -public class IotSceneRuleTimeHelper { - - /** - * 时间格式化器 - HH:mm:ss - */ - private static final DateTimeFormatter TIME_FORMATTER = DateTimeFormatter.ofPattern("HH:mm:ss"); - - /** - * 时间格式化器 - HH:mm - */ - private static final DateTimeFormatter TIME_FORMATTER_SHORT = DateTimeFormatter.ofPattern("HH:mm"); - - // TODO @puhui999:可以使用 lombok 简化 - private IotSceneRuleTimeHelper() { - // 工具类,禁止实例化 - } - - /** - * 判断是否为日期时间操作符 - * - * @param operatorEnum 操作符枚举 - * @return 是否为日期时间操作符 - */ - public static boolean isDateTimeOperator(IotSceneRuleConditionOperatorEnum operatorEnum) { - return operatorEnum == IotSceneRuleConditionOperatorEnum.DATE_TIME_GREATER_THAN - || operatorEnum == IotSceneRuleConditionOperatorEnum.DATE_TIME_LESS_THAN - || operatorEnum == IotSceneRuleConditionOperatorEnum.DATE_TIME_BETWEEN; - } - - /** - * 判断是否为时间操作符(包括日期时间操作符和当日时间操作符) - * - * @param operatorEnum 操作符枚举 - * @return 是否为时间操作符 - */ - public static boolean isTimeOperator(IotSceneRuleConditionOperatorEnum operatorEnum) { - return operatorEnum != IotSceneRuleConditionOperatorEnum.TIME_GREATER_THAN - && operatorEnum != IotSceneRuleConditionOperatorEnum.TIME_LESS_THAN - && operatorEnum != IotSceneRuleConditionOperatorEnum.TIME_BETWEEN - && !isDateTimeOperator(operatorEnum); - } - - /** - * 执行时间匹配逻辑 - * - * @param operatorEnum 操作符枚举 - * @param param 参数值 - * @return 是否匹配 - */ - public static boolean executeTimeMatching(IotSceneRuleConditionOperatorEnum operatorEnum, String param) { - try { - LocalDateTime now = LocalDateTime.now(); - if (isDateTimeOperator(operatorEnum)) { - // 日期时间匹配(时间戳,秒级) - long currentTimestamp = now.atZone(ZoneId.systemDefault()).toEpochSecond(); - return matchDateTime(currentTimestamp, operatorEnum, param); - } else { - // 当日时间匹配(HH:mm:ss) - return matchTime(now.toLocalTime(), operatorEnum, param); - } - } catch (Exception e) { - log.error("[executeTimeMatching][operatorEnum({}) param({}) 时间匹配异常]", operatorEnum, param, e); - return false; - } - } - - /** - * 匹配日期时间(时间戳,秒级) - * - * @param currentTimestamp 当前时间戳 - * @param operatorEnum 操作符枚举 - * @param param 参数值 - * @return 是否匹配 - */ - @SuppressWarnings("EnhancedSwitchMigration") - public static boolean matchDateTime(long currentTimestamp, IotSceneRuleConditionOperatorEnum operatorEnum, - String param) { - try { - // DATE_TIME_BETWEEN 需要解析两个时间戳,单独处理 - if (operatorEnum == IotSceneRuleConditionOperatorEnum.DATE_TIME_BETWEEN) { - return matchDateTimeBetween(currentTimestamp, param); - } - // 其他操作符只需要解析一个时间戳 - long targetTimestamp = Long.parseLong(param); - switch (operatorEnum) { - case DATE_TIME_GREATER_THAN: - return currentTimestamp > targetTimestamp; - case DATE_TIME_LESS_THAN: - return currentTimestamp < targetTimestamp; - default: - log.warn("[matchDateTime][operatorEnum({}) 不支持的日期时间操作符]", operatorEnum); - return false; - } - } catch (Exception e) { - log.error("[matchDateTime][operatorEnum({}) param({}) 日期时间匹配异常]", operatorEnum, param, e); - return false; - } - } - - /** - * 匹配日期时间区间 - * - * @param currentTimestamp 当前时间戳 - * @param param 参数值(格式:startTimestamp,endTimestamp) - * @return 是否匹配 - */ - public static boolean matchDateTimeBetween(long currentTimestamp, String param) { - List timestampRange = StrUtil.splitTrim(param, CharPool.COMMA); - if (timestampRange.size() != 2) { - log.warn("[matchDateTimeBetween][param({}) 时间戳区间参数格式错误]", param); - return false; - } - long startTimestamp = Long.parseLong(timestampRange.get(0).trim()); - long endTimestamp = Long.parseLong(timestampRange.get(1).trim()); - // TODO @puhui999:hutool 里,看看有没 between 方法 - return currentTimestamp >= startTimestamp && currentTimestamp <= endTimestamp; - } - - /** - * 匹配当日时间(HH:mm:ss 或 HH:mm) - * - * @param currentTime 当前时间 - * @param operatorEnum 操作符枚举 - * @param param 参数值 - * @return 是否匹配 - */ - @SuppressWarnings("EnhancedSwitchMigration") - public static boolean matchTime(LocalTime currentTime, IotSceneRuleConditionOperatorEnum operatorEnum, - String param) { - try { - // TIME_BETWEEN 需要解析两个时间,单独处理 - if (operatorEnum == IotSceneRuleConditionOperatorEnum.TIME_BETWEEN) { - return matchTimeBetween(currentTime, param); - } - // 其他操作符只需要解析一个时间 - LocalTime targetTime = parseTime(param); - switch (operatorEnum) { - case TIME_GREATER_THAN: - return currentTime.isAfter(targetTime); - case TIME_LESS_THAN: - return currentTime.isBefore(targetTime); - default: - log.warn("[matchTime][operatorEnum({}) 不支持的时间操作符]", operatorEnum); - return false; - } - } catch (Exception e) { - log.error("[matchTime][operatorEnum({}) param({}) 时间解析异常]", operatorEnum, param, e); - return false; - } - } - - /** - * 匹配时间区间 - * - * @param currentTime 当前时间 - * @param param 参数值(格式:startTime,endTime) - * @return 是否匹配 - */ - public static boolean matchTimeBetween(LocalTime currentTime, String param) { - List timeRange = StrUtil.splitTrim(param, CharPool.COMMA); - if (timeRange.size() != 2) { - log.warn("[matchTimeBetween][param({}) 时间区间参数格式错误]", param); - return false; - } - LocalTime startTime = parseTime(timeRange.get(0).trim()); - LocalTime endTime = parseTime(timeRange.get(1).trim()); - // TODO @puhui999:hutool 里,看看有没 between 方法 - return !currentTime.isBefore(startTime) && !currentTime.isAfter(endTime); - } - - /** - * 解析时间字符串 - * 支持 HH:mm 和 HH:mm:ss 两种格式 - * - * @param timeStr 时间字符串 - * @return 解析后的 LocalTime - */ - public static LocalTime parseTime(String timeStr) { - Assert.isFalse(StrUtil.isBlank(timeStr), "时间字符串不能为空"); - try { - // 尝试不同的时间格式 - if (timeStr.length() == 5) { // HH:mm - return LocalTime.parse(timeStr, TIME_FORMATTER_SHORT); - } else if (timeStr.length() == 8) { // HH:mm:ss - return LocalTime.parse(timeStr, TIME_FORMATTER); - } else { - throw new IllegalArgumentException("时间格式长度不正确,期望 HH:mm 或 HH:mm:ss 格式"); - } - } catch (Exception e) { - log.error("[parseTime][timeStr({}) 时间格式解析失败]", timeStr, e); - throw new IllegalArgumentException("时间格式无效: " + timeStr, e); - } - } - -} diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/rule/scene/action/IotAlertRecoverSceneRuleAction.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/rule/scene/action/IotAlertRecoverSceneRuleAction.java deleted file mode 100644 index a411a69..0000000 --- a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/rule/scene/action/IotAlertRecoverSceneRuleAction.java +++ /dev/null @@ -1,48 +0,0 @@ -package cn.iocoder.yudao.module.iot.service.rule.scene.action; - -import cn.hutool.core.collection.CollUtil; -import cn.hutool.core.util.StrUtil; -import cn.iocoder.yudao.module.iot.core.mq.message.IotDeviceMessage; -import cn.iocoder.yudao.module.iot.dal.dataobject.alert.IotAlertRecordDO; -import cn.iocoder.yudao.module.iot.dal.dataobject.rule.IotSceneRuleDO; -import cn.iocoder.yudao.module.iot.enums.rule.IotSceneRuleActionTypeEnum; -import cn.iocoder.yudao.module.iot.service.alert.IotAlertRecordService; -import org.springframework.stereotype.Component; - -import javax.annotation.Resource; -import java.util.List; - -import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertList; - -/** - * IoT 告警恢复的 {@link IotSceneRuleAction} 实现类 - * - * @author 芋道源码 - */ -@Component -public class IotAlertRecoverSceneRuleAction implements IotSceneRuleAction { - - private static final String PROCESS_REMARK = "告警自动回复,基于【{}】场景联动规则"; - - @Resource - private IotAlertRecordService alertRecordService; - - @Override - public void execute(IotDeviceMessage message, - IotSceneRuleDO rule, IotSceneRuleDO.Action actionConfig) throws Exception { - Long deviceId = message != null ? message.getDeviceId() : null; - List alertRecords = alertRecordService.getAlertRecordListBySceneRuleId( - rule.getId(), deviceId, false); - if (CollUtil.isEmpty(alertRecords)) { - return; - } - alertRecordService.processAlertRecordList(convertList(alertRecords, IotAlertRecordDO::getId), - StrUtil.format(PROCESS_REMARK, rule.getName())); - } - - @Override - public IotSceneRuleActionTypeEnum getType() { - return IotSceneRuleActionTypeEnum.ALERT_RECOVER; - } - -} diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/rule/scene/action/IotAlertTriggerSceneRuleAction.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/rule/scene/action/IotAlertTriggerSceneRuleAction.java deleted file mode 100644 index 3c806b6..0000000 --- a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/rule/scene/action/IotAlertTriggerSceneRuleAction.java +++ /dev/null @@ -1,68 +0,0 @@ -package cn.iocoder.yudao.module.iot.service.rule.scene.action; - -import cn.hutool.core.collection.CollUtil; -import cn.iocoder.yudao.framework.common.enums.CommonStatusEnum; -import cn.iocoder.yudao.module.iot.core.mq.message.IotDeviceMessage; -import cn.iocoder.yudao.module.iot.dal.dataobject.alert.IotAlertConfigDO; -import cn.iocoder.yudao.module.iot.dal.dataobject.rule.IotSceneRuleDO; -import cn.iocoder.yudao.module.iot.enums.rule.IotSceneRuleActionTypeEnum; -import cn.iocoder.yudao.module.iot.service.alert.IotAlertConfigService; -import cn.iocoder.yudao.module.iot.service.alert.IotAlertRecordService; -import cn.iocoder.yudao.module.system.api.mail.MailSendApi; -import cn.iocoder.yudao.module.system.api.notify.NotifyMessageSendApi; -import cn.iocoder.yudao.module.system.api.sms.SmsSendApi; -import org.springframework.stereotype.Component; - -import javax.annotation.Nullable; -import javax.annotation.Resource; -import java.util.List; - -/** - * IoT 告警触发的 {@link IotSceneRuleAction} 实现类 - * - * @author 芋道源码 - */ -@Component -public class IotAlertTriggerSceneRuleAction implements IotSceneRuleAction { - - @Resource - private IotAlertConfigService alertConfigService; - @Resource - private IotAlertRecordService alertRecordService; - - @Resource - private SmsSendApi smsSendApi; - @Resource - private MailSendApi mailSendApi; - @Resource - private NotifyMessageSendApi notifyMessageSendApi; - - @Override - public void execute(@Nullable IotDeviceMessage message, - IotSceneRuleDO rule, IotSceneRuleDO.Action actionConfig) throws Exception { - List alertConfigs = alertConfigService.getAlertConfigListBySceneRuleIdAndStatus( - rule.getId(), CommonStatusEnum.ENABLE.getStatus()); - if (CollUtil.isEmpty(alertConfigs)) { - return; - } - alertConfigs.forEach(alertConfig -> { - // 记录告警记录,传递场景规则ID - alertRecordService.createAlertRecord(alertConfig, rule.getId(), message); - // 发送告警消息 - sendAlertMessage(alertConfig, message); - }); - } - - private void sendAlertMessage(IotAlertConfigDO config, IotDeviceMessage deviceMessage) { - // TODO @芋艿:等场景联动开发完,再实现 - // TODO @芋艿:短信 - // TODO @芋艿:邮箱 - // TODO @芋艿:站内信 - } - - @Override - public IotSceneRuleActionTypeEnum getType() { - return IotSceneRuleActionTypeEnum.ALERT_TRIGGER; - } - -} diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/rule/scene/action/IotDevicePropertySetSceneRuleAction.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/rule/scene/action/IotDevicePropertySetSceneRuleAction.java deleted file mode 100644 index 6a48943..0000000 --- a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/rule/scene/action/IotDevicePropertySetSceneRuleAction.java +++ /dev/null @@ -1,142 +0,0 @@ -package cn.iocoder.yudao.module.iot.service.rule.scene.action; - -import cn.hutool.core.collection.CollUtil; -import cn.hutool.core.map.MapUtil; -import cn.hutool.core.util.StrUtil; -import cn.iocoder.yudao.module.iot.core.enums.IotDeviceMessageMethodEnum; -import cn.iocoder.yudao.module.iot.core.mq.message.IotDeviceMessage; -import cn.iocoder.yudao.module.iot.dal.dataobject.device.IotDeviceDO; -import cn.iocoder.yudao.module.iot.dal.dataobject.rule.IotSceneRuleDO; -import cn.iocoder.yudao.module.iot.enums.rule.IotSceneRuleActionTypeEnum; -import cn.iocoder.yudao.module.iot.service.device.IotDeviceService; -import cn.iocoder.yudao.module.iot.service.device.message.IotDeviceMessageService; -import lombok.extern.slf4j.Slf4j; -import org.springframework.stereotype.Component; - -import javax.annotation.Resource; -import java.util.List; - -/** - * IoT 设备属性设置的 {@link IotSceneRuleAction} 实现类 - * - * @author 芋道源码 - */ -@Component -@Slf4j -public class IotDevicePropertySetSceneRuleAction implements IotSceneRuleAction { - - @Resource - private IotDeviceService deviceService; - @Resource - private IotDeviceMessageService deviceMessageService; - - @Override - public void execute(IotDeviceMessage message, - IotSceneRuleDO rule, IotSceneRuleDO.Action actionConfig) { - // 1. 参数校验 - if (actionConfig.getDeviceId() == null) { - log.error("[execute][规则场景({}) 动作配置({}) 设备编号不能为空]", rule.getId(), actionConfig); - return; - } - if (StrUtil.isEmpty(actionConfig.getIdentifier())) { - log.error("[execute][规则场景({}) 动作配置({}) 属性标识符不能为空]", rule.getId(), actionConfig); - return; - } - - // 2. 判断是否为全部设备 - if (IotDeviceDO.DEVICE_ID_ALL.equals(actionConfig.getDeviceId())) { - executeForAllDevices(message, rule, actionConfig); - } else { - executeForSingleDevice(message, rule, actionConfig); - } - } - - /** - * 为单个设备执行属性设置 - */ - private void executeForSingleDevice(IotDeviceMessage message, - IotSceneRuleDO rule, IotSceneRuleDO.Action actionConfig) { - // 1. 获取设备信息 - IotDeviceDO device = deviceService.getDeviceFromCache(actionConfig.getDeviceId()); - if (device == null) { - log.error("[executeForSingleDevice][规则场景({}) 动作配置({}) 对应的设备({}) 不存在]", - rule.getId(), actionConfig, actionConfig.getDeviceId()); - return; - } - - // 2. 执行属性设置 - executePropertySetForDevice(rule, actionConfig, device); - } - - /** - * 为产品下的所有设备执行属性设置 - */ - private void executeForAllDevices(IotDeviceMessage message, - IotSceneRuleDO rule, IotSceneRuleDO.Action actionConfig) { - // 1. 参数校验 - if (actionConfig.getProductId() == null) { - log.error("[executeForAllDevices][规则场景({}) 动作配置({}) 产品编号不能为空]", rule.getId(), actionConfig); - return; - } - - // 2. 获取产品下的所有设备 - List devices = deviceService.getDeviceListByProductId(actionConfig.getProductId()); - if (CollUtil.isEmpty(devices)) { - log.warn("[executeForAllDevices][规则场景({}) 动作配置({}) 产品({}) 下没有设备]", - rule.getId(), actionConfig, actionConfig.getProductId()); - return; - } - - // 3. 遍历所有设备执行属性设置 - for (IotDeviceDO device : devices) { - executePropertySetForDevice(rule, actionConfig, device); - } - } - - /** - * 为指定设备执行属性设置 - */ - private void executePropertySetForDevice(IotSceneRuleDO rule, IotSceneRuleDO.Action actionConfig, IotDeviceDO device) { - // 1. 构建属性设置消息 - IotDeviceMessage downstreamMessage = buildPropertySetMessage(actionConfig, device); - if (downstreamMessage == null) { - log.error("[executePropertySetForDevice][规则场景({}) 动作配置({}) 设备({}) 构建属性设置消息失败]", - rule.getId(), actionConfig, device.getId()); - return; - } - - // 2. 发送设备消息 - try { - IotDeviceMessage result = deviceMessageService.sendDeviceMessage(downstreamMessage, device); - log.info("[executePropertySetForDevice][规则场景({}) 动作配置({}) 设备({}) 属性设置消息({}) 发送成功]", - rule.getId(), actionConfig, device.getId(), result.getId()); - } catch (Exception e) { - log.error("[executePropertySetForDevice][规则场景({}) 动作配置({}) 设备({}) 属性设置消息发送失败]", - rule.getId(), actionConfig, device.getId(), e); - } - } - - /** - * 构建属性设置消息 - * - * @param actionConfig 动作配置 - * @param device 设备信息 - * @return 设备消息 - */ - private IotDeviceMessage buildPropertySetMessage(IotSceneRuleDO.Action actionConfig, IotDeviceDO device) { - try { - // 属性设置参数格式: {"properties": {"identifier": value}} - Object params = MapUtil.of("properties", MapUtil.of(actionConfig.getIdentifier(), actionConfig.getParams())); - return IotDeviceMessage.requestOf(IotDeviceMessageMethodEnum.PROPERTY_SET.getMethod(), params); - } catch (Exception e) { - log.error("[buildPropertySetMessage][构建属性设置消息异常]", e); - return null; - } - } - - @Override - public IotSceneRuleActionTypeEnum getType() { - return IotSceneRuleActionTypeEnum.DEVICE_PROPERTY_SET; - } - -} diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/rule/scene/action/IotDeviceServiceInvokeSceneRuleAction.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/rule/scene/action/IotDeviceServiceInvokeSceneRuleAction.java deleted file mode 100644 index d0b1ffe..0000000 --- a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/rule/scene/action/IotDeviceServiceInvokeSceneRuleAction.java +++ /dev/null @@ -1,146 +0,0 @@ -package cn.iocoder.yudao.module.iot.service.rule.scene.action; - -import cn.hutool.core.collection.CollUtil; -import cn.hutool.core.map.MapUtil; -import cn.hutool.core.util.StrUtil; -import cn.iocoder.yudao.module.iot.core.enums.IotDeviceMessageMethodEnum; -import cn.iocoder.yudao.module.iot.core.mq.message.IotDeviceMessage; -import cn.iocoder.yudao.module.iot.dal.dataobject.device.IotDeviceDO; -import cn.iocoder.yudao.module.iot.dal.dataobject.rule.IotSceneRuleDO; -import cn.iocoder.yudao.module.iot.enums.rule.IotSceneRuleActionTypeEnum; -import cn.iocoder.yudao.module.iot.service.device.IotDeviceService; -import cn.iocoder.yudao.module.iot.service.device.message.IotDeviceMessageService; -import lombok.extern.slf4j.Slf4j; -import org.springframework.stereotype.Component; - -import javax.annotation.Resource; -import java.util.Collections; -import java.util.List; - -/** - * IoT 设备服务调用的 {@link IotSceneRuleAction} 实现类 - * - * @author HUIHUI - */ -@Component -@Slf4j -public class IotDeviceServiceInvokeSceneRuleAction implements IotSceneRuleAction { - - @Resource - private IotDeviceService deviceService; - @Resource - private IotDeviceMessageService deviceMessageService; - - @Override - public void execute(IotDeviceMessage message, - IotSceneRuleDO rule, IotSceneRuleDO.Action actionConfig) { - // 1. 参数校验 - if (actionConfig.getDeviceId() == null) { - log.error("[execute][规则场景({}) 动作配置({}) 设备编号不能为空]", rule.getId(), actionConfig); - return; - } - if (StrUtil.isEmpty(actionConfig.getIdentifier())) { - log.error("[execute][规则场景({}) 动作配置({}) 服务标识符不能为空]", rule.getId(), actionConfig); - return; - } - - // 2. 判断是否为全部设备 - if (IotDeviceDO.DEVICE_ID_ALL.equals(actionConfig.getDeviceId())) { - executeForAllDevices(message, rule, actionConfig); - } else { - executeForSingleDevice(message, rule, actionConfig); - } - } - - /** - * 为单个设备执行服务调用 - */ - private void executeForSingleDevice(IotDeviceMessage message, - IotSceneRuleDO rule, IotSceneRuleDO.Action actionConfig) { - // 1. 获取设备信息 - IotDeviceDO device = deviceService.getDeviceFromCache(actionConfig.getDeviceId()); - if (device == null) { - log.error("[executeForSingleDevice][规则场景({}) 动作配置({}) 对应的设备({}) 不存在]", - rule.getId(), actionConfig, actionConfig.getDeviceId()); - return; - } - - // 2. 执行服务调用 - executeServiceInvokeForDevice(rule, actionConfig, device); - } - - /** - * 为产品下的所有设备执行服务调用 - */ - private void executeForAllDevices(IotDeviceMessage message, - IotSceneRuleDO rule, IotSceneRuleDO.Action actionConfig) { - // 1. 参数校验 - if (actionConfig.getProductId() == null) { - log.error("[executeForAllDevices][规则场景({}) 动作配置({}) 产品编号不能为空]", rule.getId(), actionConfig); - return; - } - - // 2. 获取产品下的所有设备 - List devices = deviceService.getDeviceListByProductId(actionConfig.getProductId()); - if (CollUtil.isEmpty(devices)) { - log.warn("[executeForAllDevices][规则场景({}) 动作配置({}) 产品({}) 下没有设备]", - rule.getId(), actionConfig, actionConfig.getProductId()); - return; - } - - // 3. 遍历所有设备执行服务调用 - for (IotDeviceDO device : devices) { - executeServiceInvokeForDevice(rule, actionConfig, device); - } - } - - /** - * 为指定设备执行服务调用 - */ - private void executeServiceInvokeForDevice(IotSceneRuleDO rule, IotSceneRuleDO.Action actionConfig, IotDeviceDO device) { - // 1. 构建服务调用消息 - IotDeviceMessage downstreamMessage = buildServiceInvokeMessage(actionConfig, device); - if (downstreamMessage == null) { - log.error("[executeServiceInvokeForDevice][规则场景({}) 动作配置({}) 设备({}) 构建服务调用消息失败]", - rule.getId(), actionConfig, device.getId()); - return; - } - - // 2. 发送设备消息 - try { - IotDeviceMessage result = deviceMessageService.sendDeviceMessage(downstreamMessage, device); - log.info("[executeServiceInvokeForDevice][规则场景({}) 动作配置({}) 设备({}) 服务调用消息({}) 发送成功]", - rule.getId(), actionConfig, device.getId(), result.getId()); - } catch (Exception e) { - log.error("[executeServiceInvokeForDevice][规则场景({}) 动作配置({}) 设备({}) 服务调用消息发送失败]", - rule.getId(), actionConfig, device.getId(), e); - } - } - - /** - * 构建服务调用消息 - * - * @param actionConfig 动作配置 - * @param device 设备信息 - * @return 设备消息 - */ - private IotDeviceMessage buildServiceInvokeMessage(IotSceneRuleDO.Action actionConfig, IotDeviceDO device) { - try { - // 服务调用参数格式: {"identifier": "serviceId", "params": {...}} - Object params = MapUtil.builder() - .put("identifier", actionConfig.getIdentifier()) - .put("params", actionConfig.getParams() != null ? actionConfig.getParams() : Collections.emptyMap()) - .build(); - return IotDeviceMessage.requestOf(IotDeviceMessageMethodEnum.SERVICE_INVOKE.getMethod(), params); - } catch (Exception e) { - log.error("[buildServiceInvokeMessage][构建服务调用消息异常]", e); - return null; - } - } - - @Override - public IotSceneRuleActionTypeEnum getType() { - return IotSceneRuleActionTypeEnum.DEVICE_SERVICE_INVOKE; - } - -} diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/rule/scene/action/IotSceneRuleAction.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/rule/scene/action/IotSceneRuleAction.java deleted file mode 100644 index c88a37f..0000000 --- a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/rule/scene/action/IotSceneRuleAction.java +++ /dev/null @@ -1,36 +0,0 @@ -package cn.iocoder.yudao.module.iot.service.rule.scene.action; - -import cn.iocoder.yudao.module.iot.core.mq.message.IotDeviceMessage; -import cn.iocoder.yudao.module.iot.dal.dataobject.rule.IotSceneRuleDO; -import cn.iocoder.yudao.module.iot.enums.rule.IotSceneRuleActionTypeEnum; - -import javax.annotation.Nullable; - -/** - * IoT 场景联动的执行器接口 - * - * @author 芋道源码 - */ -public interface IotSceneRuleAction { - - /** - * 执行场景联动 - * - * @param message 消息,允许空 - * 1. 空的情况:定时触发 - * 2. 非空的情况:设备触发 - * @param rule 规则 - * @param actionConfig 执行配置(实际对应规则里的哪条执行配置) - */ - void execute(@Nullable IotDeviceMessage message, - IotSceneRuleDO rule, - IotSceneRuleDO.Action actionConfig) throws Exception; - - /** - * 获得类型 - * - * @return 类型 - */ - IotSceneRuleActionTypeEnum getType(); - -} diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/rule/scene/matcher/IotSceneRuleMatcher.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/rule/scene/matcher/IotSceneRuleMatcher.java deleted file mode 100644 index cf312be..0000000 --- a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/rule/scene/matcher/IotSceneRuleMatcher.java +++ /dev/null @@ -1,38 +0,0 @@ -package cn.iocoder.yudao.module.iot.service.rule.scene.matcher; - -import cn.iocoder.yudao.module.iot.service.rule.scene.matcher.condition.IotSceneRuleConditionMatcher; -import cn.iocoder.yudao.module.iot.service.rule.scene.matcher.trigger.IotSceneRuleTriggerMatcher; - -/** - * IoT 场景规则匹配器基础接口:定义所有匹配器的通用行为,包括优先级、名称和启用状态 - * - * - {@link IotSceneRuleTriggerMatcher} 触发器匹配器 - * - {@link IotSceneRuleConditionMatcher} 条件匹配器 - * - * @author HUIHUI - */ -public interface IotSceneRuleMatcher { - - /** - * 获取匹配优先级(数值越小优先级越高) - *

- * 用于在多个匹配器支持同一类型时确定优先级 - * - * @return 优先级数值 - */ - default int getPriority() { - return 100; - } - - /** - * 是否启用该匹配器 - *

- * 可用于动态开关某些匹配器 - * - * @return 是否启用 - */ - default boolean isEnabled() { - return true; - } - -} diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/rule/scene/matcher/IotSceneRuleMatcherHelper.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/rule/scene/matcher/IotSceneRuleMatcherHelper.java deleted file mode 100644 index 937add3..0000000 --- a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/rule/scene/matcher/IotSceneRuleMatcherHelper.java +++ /dev/null @@ -1,236 +0,0 @@ -package cn.iocoder.yudao.module.iot.service.rule.scene.matcher; - -import cn.hutool.core.text.CharPool; -import cn.hutool.core.util.NumberUtil; -import cn.hutool.core.util.StrUtil; -import cn.iocoder.yudao.framework.common.util.number.NumberUtils; -import cn.iocoder.yudao.framework.common.util.object.ObjectUtils; -import cn.iocoder.yudao.framework.common.util.spring.SpringExpressionUtils; -import cn.iocoder.yudao.module.iot.core.mq.message.IotDeviceMessage; -import cn.iocoder.yudao.module.iot.dal.dataobject.rule.IotSceneRuleDO; -import cn.iocoder.yudao.module.iot.enums.rule.IotSceneRuleConditionOperatorEnum; -import lombok.extern.slf4j.Slf4j; - -import java.util.HashMap; -import java.util.List; -import java.util.Map; - -import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertList; - -/** - * IoT 场景规则匹配器工具类:提供通用的条件评估逻辑和工具方法,供触发器和条件匹配器使用 - * - * 该类包含了匹配器实现中常用的工具方法,如条件评估、参数校验、日志记录等 - * - * @author HUIHUI - */ -@Slf4j -public final class IotSceneRuleMatcherHelper { - - /** - * 私有构造函数,防止实例化 - */ - private IotSceneRuleMatcherHelper() { - } - - /** - * 评估条件是否匹配 - * - * @param sourceValue 源值(来自消息) - * @param operator 操作符 - * @param paramValue 参数值(来自条件配置) - * @return 是否匹配 - */ - public static boolean evaluateCondition(Object sourceValue, String operator, String paramValue) { - try { - // 1. 校验操作符是否合法 - IotSceneRuleConditionOperatorEnum operatorEnum = IotSceneRuleConditionOperatorEnum.operatorOf(operator); - if (operatorEnum == null) { - log.warn("[evaluateCondition][operator({}) 操作符无效]", operator); - return false; - } - - // 2. 构建 Spring 表达式变量 - return evaluateConditionWithOperatorEnum(sourceValue, operatorEnum, paramValue); - } catch (Exception e) { - log.error("[evaluateCondition][sourceValue({}) operator({}) paramValue({}) 条件评估异常]", - sourceValue, operator, paramValue, e); - return false; - } - } - - /** - * 使用操作符枚举评估条件是否匹配 - * - * @param sourceValue 源值(来自消息) - * @param operatorEnum 操作符枚举 - * @param paramValue 参数值(来自条件配置) - * @return 是否匹配 - */ - @SuppressWarnings("DataFlowIssue") - public static boolean evaluateConditionWithOperatorEnum(Object sourceValue, IotSceneRuleConditionOperatorEnum operatorEnum, String paramValue) { - try { - // 1. 构建 Spring 表达式变量 - Map springExpressionVariables = buildSpringExpressionVariables(sourceValue, operatorEnum, paramValue); - - // 2. 计算 Spring 表达式 - return (Boolean) SpringExpressionUtils.parseExpression(operatorEnum.getSpringExpression(), springExpressionVariables); - } catch (Exception e) { - log.error("[evaluateConditionWithOperatorEnum][sourceValue({}) operatorEnum({}) paramValue({}) 条件评估异常]", - sourceValue, operatorEnum, paramValue, e); - return false; - } - } - - /** - * 构建 Spring 表达式变量 - */ - private static Map buildSpringExpressionVariables(Object sourceValue, IotSceneRuleConditionOperatorEnum operatorEnum, String paramValue) { - Map springExpressionVariables = new HashMap<>(); - - // 设置源值 - springExpressionVariables.put(IotSceneRuleConditionOperatorEnum.SPRING_EXPRESSION_SOURCE, StrUtil.toString(sourceValue)); - - // 处理参数值 - if (StrUtil.isNotBlank(paramValue)) { - List parameterValues = StrUtil.splitTrim(paramValue, CharPool.COMMA); - - // 设置原始参数值 - springExpressionVariables.put(IotSceneRuleConditionOperatorEnum.SPRING_EXPRESSION_VALUE, paramValue); - springExpressionVariables.put(IotSceneRuleConditionOperatorEnum.SPRING_EXPRESSION_VALUE_LIST, parameterValues); - - // 特殊处理:解决数字比较问题 - // Spring 表达式基于 compareTo 方法,对数字的比较存在问题,需要转换为数字类型 - if (isNumericComparisonOperator(operatorEnum) && isNumericComparison(sourceValue, parameterValues)) { - springExpressionVariables.put(IotSceneRuleConditionOperatorEnum.SPRING_EXPRESSION_SOURCE, - NumberUtil.parseDouble(String.valueOf(sourceValue))); - springExpressionVariables.put(IotSceneRuleConditionOperatorEnum.SPRING_EXPRESSION_VALUE, - NumberUtil.parseDouble(paramValue)); - springExpressionVariables.put(IotSceneRuleConditionOperatorEnum.SPRING_EXPRESSION_VALUE_LIST, - convertList(parameterValues, NumberUtil::parseDouble)); - } - } - - return springExpressionVariables; - } - - /** - * 判断是否为数字比较操作符 - */ - private static boolean isNumericComparisonOperator(IotSceneRuleConditionOperatorEnum operatorEnum) { - return ObjectUtils.equalsAny(operatorEnum, - IotSceneRuleConditionOperatorEnum.BETWEEN, - IotSceneRuleConditionOperatorEnum.NOT_BETWEEN, - IotSceneRuleConditionOperatorEnum.GREATER_THAN, - IotSceneRuleConditionOperatorEnum.GREATER_THAN_OR_EQUALS, - IotSceneRuleConditionOperatorEnum.LESS_THAN, - IotSceneRuleConditionOperatorEnum.LESS_THAN_OR_EQUALS); - } - - /** - * 判断是否为数字比较场景 - */ - private static boolean isNumericComparison(Object sourceValue, List parameterValues) { - return NumberUtil.isNumber(String.valueOf(sourceValue)) && NumberUtils.isAllNumber(parameterValues); - } - - // ========== 【触发器】相关工具方法 ========== - - /** - * 检查基础触发器参数是否有效 - * - * @param trigger 触发器配置 - * @return 是否有效 - */ - public static boolean isBasicTriggerValid(IotSceneRuleDO.Trigger trigger) { - return trigger != null && trigger.getType() != null; - } - - /** - * 检查触发器操作符和值是否有效 - * - * @param trigger 触发器配置 - * @return 是否有效 - */ - public static boolean isTriggerOperatorAndValueValid(IotSceneRuleDO.Trigger trigger) { - return StrUtil.isNotBlank(trigger.getOperator()) && StrUtil.isNotBlank(trigger.getValue()); - } - - /** - * 记录触发器匹配成功日志 - * - * @param message 设备消息 - * @param trigger 触发器配置 - */ - public static void logTriggerMatchSuccess(IotDeviceMessage message, IotSceneRuleDO.Trigger trigger) { - log.debug("[isMatched][message({}) trigger({}) 匹配触发器成功]", message.getRequestId(), trigger.getType()); - } - - /** - * 记录触发器匹配失败日志 - * - * @param message 设备消息 - * @param trigger 触发器配置 - * @param reason 失败原因 - */ - public static void logTriggerMatchFailure(IotDeviceMessage message, IotSceneRuleDO.Trigger trigger, String reason) { - log.debug("[isMatched][message({}) trigger({}) reason({}) 匹配触发器失败]", message.getRequestId(), trigger.getType(), reason); - } - - // ========== 【条件】相关工具方法 ========== - - /** - * 检查基础条件参数是否有效 - * - * @param condition 触发条件 - * @return 是否有效 - */ - public static boolean isBasicConditionValid(IotSceneRuleDO.TriggerCondition condition) { - return condition != null && condition.getType() != null; - } - - /** - * 检查条件操作符和参数是否有效 - * - * @param condition 触发条件 - * @return 是否有效 - */ - public static boolean isConditionOperatorAndParamValid(IotSceneRuleDO.TriggerCondition condition) { - return StrUtil.isNotBlank(condition.getOperator()) && StrUtil.isNotBlank(condition.getParam()); - } - - /** - * 记录条件匹配成功日志 - * - * @param message 设备消息 - * @param condition 触发条件 - */ - public static void logConditionMatchSuccess(IotDeviceMessage message, IotSceneRuleDO.TriggerCondition condition) { - log.debug("[isMatched][message({}) condition({}) 匹配条件成功]", message.getRequestId(), condition.getType()); - } - - /** - * 记录条件匹配失败日志 - * - * @param message 设备消息 - * @param condition 触发条件 - * @param reason 失败原因 - */ - public static void logConditionMatchFailure(IotDeviceMessage message, IotSceneRuleDO.TriggerCondition condition, String reason) { - log.debug("[isMatched][message({}) condition({}) reason({}) 匹配条件失败]", message.getRequestId(), condition.getType(), reason); - } - - // ========== 【通用】工具方法 ========== - - /** - * 检查标识符是否匹配 - * - * @param expectedIdentifier 期望的标识符 - * @param actualIdentifier 实际的标识符 - * @return 是否匹配 - */ - public static boolean isIdentifierMatched(String expectedIdentifier, String actualIdentifier) { - return StrUtil.isNotBlank(expectedIdentifier) && expectedIdentifier.equals(actualIdentifier); - } - -} diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/rule/scene/matcher/IotSceneRuleMatcherManager.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/rule/scene/matcher/IotSceneRuleMatcherManager.java deleted file mode 100644 index 3333269..0000000 --- a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/rule/scene/matcher/IotSceneRuleMatcherManager.java +++ /dev/null @@ -1,157 +0,0 @@ -package cn.iocoder.yudao.module.iot.service.rule.scene.matcher; - -import cn.hutool.core.collection.CollUtil; -import cn.iocoder.yudao.module.iot.core.mq.message.IotDeviceMessage; -import cn.iocoder.yudao.module.iot.dal.dataobject.rule.IotSceneRuleDO; -import cn.iocoder.yudao.module.iot.enums.rule.IotSceneRuleConditionTypeEnum; -import cn.iocoder.yudao.module.iot.enums.rule.IotSceneRuleTriggerTypeEnum; -import cn.iocoder.yudao.module.iot.service.rule.scene.matcher.condition.IotSceneRuleConditionMatcher; -import cn.iocoder.yudao.module.iot.service.rule.scene.matcher.trigger.IotSceneRuleTriggerMatcher; -import lombok.extern.slf4j.Slf4j; -import org.springframework.stereotype.Component; - -import java.util.*; -import java.util.function.Function; -import java.util.stream.Collectors; - -import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertMap; - -/** - * IoT 场景规则匹配器统一管理器:负责管理所有匹配器(触发器匹配器和条件匹配器),并提供统一的匹配入口 - * - * @author HUIHUI - */ -@Component -@Slf4j -public class IotSceneRuleMatcherManager { - - /** - * 触发器匹配器映射表 - */ - private final Map triggerMatchers; - - /** - * 条件匹配器映射表 - */ - private final Map conditionMatchers; - - public IotSceneRuleMatcherManager(List matchers) { - if (CollUtil.isEmpty(matchers)) { - log.warn("[IotSceneRuleMatcherManager][没有找到任何匹配器]"); - this.triggerMatchers = new HashMap<>(); - this.conditionMatchers = new HashMap<>(); - return; - } - - // 1.1 按优先级排序并过滤启用的匹配器 - List allMatchers = matchers.stream() - .filter(IotSceneRuleMatcher::isEnabled) - .sorted(Comparator.comparing(IotSceneRuleMatcher::getPriority)) - .collect(Collectors.toList()); - // 1.2 分离触发器匹配器和条件匹配器 - List triggerMatchers = allMatchers.stream() - .filter(matcher -> matcher instanceof IotSceneRuleTriggerMatcher) - .map(matcher -> (IotSceneRuleTriggerMatcher) matcher) - .collect(Collectors.toList()); - List conditionMatchers = allMatchers.stream() - .filter(matcher -> matcher instanceof IotSceneRuleConditionMatcher) - .map(matcher -> (IotSceneRuleConditionMatcher) matcher) - .collect(Collectors.toList()); - - // 2.1 构建触发器匹配器映射表 - this.triggerMatchers = convertMap(triggerMatchers, IotSceneRuleTriggerMatcher::getSupportedTriggerType, - Function.identity(), - (existing, replacement) -> { - log.warn("[IotSceneRuleMatcherManager][触发器类型({})存在多个匹配器,使用优先级更高的: {}]", - existing.getSupportedTriggerType(), - existing.getPriority() <= replacement.getPriority() ? - existing.getSupportedTriggerType() : replacement.getSupportedTriggerType()); - return existing.getPriority() <= replacement.getPriority() ? existing : replacement; - }, LinkedHashMap::new); - // 2.2 构建条件匹配器映射表 - this.conditionMatchers = convertMap(conditionMatchers, IotSceneRuleConditionMatcher::getSupportedConditionType, - Function.identity(), - (existing, replacement) -> { - log.warn("[IotSceneRuleMatcherManager][条件类型({})存在多个匹配器,使用优先级更高的: {}]", - existing.getSupportedConditionType(), - existing.getPriority() <= replacement.getPriority() ? - existing.getSupportedConditionType() : replacement.getSupportedConditionType()); - return existing.getPriority() <= replacement.getPriority() ? existing : replacement; - }, - LinkedHashMap::new); - - // 3. 日志输出初始化信息 - log.info("[IotSceneRuleMatcherManager][初始化完成,共加载({})个匹配器,其中触发器匹配器({})个,条件匹配器({})个]", - allMatchers.size(), this.triggerMatchers.size(), this.conditionMatchers.size()); - this.triggerMatchers.forEach((type, matcher) -> - log.info("[IotSceneRuleMatcherManager][触发器匹配器类型: ({}), 优先级: ({})] ", type, matcher.getPriority())); - this.conditionMatchers.forEach((type, matcher) -> - log.info("[IotSceneRuleMatcherManager][条件匹配器类型: ({}), 优先级: ({})]", type, matcher.getPriority())); - } - - /** - * 检查触发器是否匹配消息(主条件匹配) - * - * @param message 设备消息 - * @param trigger 触发器配置 - * @return 是否匹配 - */ - public boolean isMatched(IotDeviceMessage message, IotSceneRuleDO.Trigger trigger) { - if (message == null || trigger == null || trigger.getType() == null) { - log.debug("[isMatched][message({}) trigger({}) 参数无效]", message, trigger); - return false; - } - IotSceneRuleTriggerTypeEnum triggerType = IotSceneRuleTriggerTypeEnum.typeOf(trigger.getType()); - if (triggerType == null) { - log.warn("[isMatched][triggerType({}) 未知的触发器类型]", trigger.getType()); - return false; - } - IotSceneRuleTriggerMatcher matcher = triggerMatchers.get(triggerType); - if (matcher == null) { - log.warn("[isMatched][triggerType({}) 没有对应的匹配器]", triggerType); - return false; - } - - try { - return matcher.matches(message, trigger); - } catch (Exception e) { - log.error("[isMatched][触发器匹配异常] message: {}, trigger: {}", message, trigger, e); - return false; - } - } - - /** - * 检查子条件是否匹配消息 - * - * @param message 设备消息 - * @param condition 触发条件 - * @return 是否匹配 - */ - public boolean isConditionMatched(IotDeviceMessage message, IotSceneRuleDO.TriggerCondition condition) { - if (message == null || condition == null || condition.getType() == null) { - log.debug("[isConditionMatched][message({}) condition({}) 参数无效]", message, condition); - return false; - } - - // 1. 根据条件类型查找对应的匹配器 - IotSceneRuleConditionTypeEnum conditionType = IotSceneRuleConditionTypeEnum.typeOf(condition.getType()); - if (conditionType == null) { - log.warn("[isConditionMatched][conditionType({}) 未知的条件类型]", condition.getType()); - return false; - } - IotSceneRuleConditionMatcher matcher = conditionMatchers.get(conditionType); - if (matcher == null) { - log.warn("[isConditionMatched][conditionType({}) 没有对应的匹配器]", conditionType); - return false; - } - - // 2. 执行匹配逻辑 - try { - return matcher.matches(message, condition); - } catch (Exception e) { - log.error("[isConditionMatched][message({}) condition({}) 条件匹配异常]", message, condition, e); - return false; - } - } - -} diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/rule/scene/matcher/condition/IotCurrentTimeConditionMatcher.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/rule/scene/matcher/condition/IotCurrentTimeConditionMatcher.java deleted file mode 100644 index a54785a..0000000 --- a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/rule/scene/matcher/condition/IotCurrentTimeConditionMatcher.java +++ /dev/null @@ -1,71 +0,0 @@ -package cn.iocoder.yudao.module.iot.service.rule.scene.matcher.condition; - -import cn.iocoder.yudao.module.iot.core.mq.message.IotDeviceMessage; -import cn.iocoder.yudao.module.iot.dal.dataobject.rule.IotSceneRuleDO; -import cn.iocoder.yudao.module.iot.enums.rule.IotSceneRuleConditionOperatorEnum; -import cn.iocoder.yudao.module.iot.enums.rule.IotSceneRuleConditionTypeEnum; -import cn.iocoder.yudao.module.iot.service.rule.scene.IotSceneRuleTimeHelper; -import cn.iocoder.yudao.module.iot.service.rule.scene.matcher.IotSceneRuleMatcherHelper; -import lombok.extern.slf4j.Slf4j; -import org.springframework.stereotype.Component; - -/** - * 当前时间条件匹配器:处理时间相关的子条件匹配逻辑 - * - * @author HUIHUI - */ -@Component -@Slf4j -public class IotCurrentTimeConditionMatcher implements IotSceneRuleConditionMatcher { - - @Override - public IotSceneRuleConditionTypeEnum getSupportedConditionType() { - return IotSceneRuleConditionTypeEnum.CURRENT_TIME; - } - - @Override - public boolean matches(IotDeviceMessage message, IotSceneRuleDO.TriggerCondition condition) { - // 1.1 基础参数校验 - if (!IotSceneRuleMatcherHelper.isBasicConditionValid(condition)) { - IotSceneRuleMatcherHelper.logConditionMatchFailure(message, condition, "条件基础参数无效"); - return false; - } - - // 1.2 检查操作符和参数是否有效 - if (!IotSceneRuleMatcherHelper.isConditionOperatorAndParamValid(condition)) { - IotSceneRuleMatcherHelper.logConditionMatchFailure(message, condition, "操作符或参数无效"); - return false; - } - - // 1.3 验证操作符是否为支持的时间操作符 - String operator = condition.getOperator(); - IotSceneRuleConditionOperatorEnum operatorEnum = IotSceneRuleConditionOperatorEnum.operatorOf(operator); - if (operatorEnum == null) { - IotSceneRuleMatcherHelper.logConditionMatchFailure(message, condition, "无效的操作符: " + operator); - return false; - } - - if (IotSceneRuleTimeHelper.isTimeOperator(operatorEnum)) { - IotSceneRuleMatcherHelper.logConditionMatchFailure(message, condition, "不支持的时间操作符: " + operator); - return false; - } - - // 2.1 执行时间匹配 - boolean matched = IotSceneRuleTimeHelper.executeTimeMatching(operatorEnum, condition.getParam()); - - // 2.2 记录匹配结果 - if (matched) { - IotSceneRuleMatcherHelper.logConditionMatchSuccess(message, condition); - } else { - IotSceneRuleMatcherHelper.logConditionMatchFailure(message, condition, "时间条件不匹配"); - } - - return matched; - } - - @Override - public int getPriority() { - return 40; // 较低优先级 - } - -} diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/rule/scene/matcher/condition/IotDevicePropertyConditionMatcher.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/rule/scene/matcher/condition/IotDevicePropertyConditionMatcher.java deleted file mode 100644 index c130c55..0000000 --- a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/rule/scene/matcher/condition/IotDevicePropertyConditionMatcher.java +++ /dev/null @@ -1,68 +0,0 @@ -package cn.iocoder.yudao.module.iot.service.rule.scene.matcher.condition; - - -import cn.iocoder.yudao.module.iot.core.mq.message.IotDeviceMessage; -import cn.iocoder.yudao.module.iot.core.util.IotDeviceMessageUtils; -import cn.iocoder.yudao.module.iot.dal.dataobject.rule.IotSceneRuleDO; -import cn.iocoder.yudao.module.iot.enums.rule.IotSceneRuleConditionTypeEnum; -import cn.iocoder.yudao.module.iot.service.rule.scene.matcher.IotSceneRuleMatcherHelper; -import org.springframework.stereotype.Component; - - -/** - * 设备属性条件匹配器:处理设备属性相关的子条件匹配逻辑 - * - * @author HUIHUI - */ -@Component -public class IotDevicePropertyConditionMatcher implements IotSceneRuleConditionMatcher { - - @Override - public IotSceneRuleConditionTypeEnum getSupportedConditionType() { - return IotSceneRuleConditionTypeEnum.DEVICE_PROPERTY; - } - - @Override - public boolean matches(IotDeviceMessage message, IotSceneRuleDO.TriggerCondition condition) { - // 1.1 基础参数校验 - if (!IotSceneRuleMatcherHelper.isBasicConditionValid(condition)) { - IotSceneRuleMatcherHelper.logConditionMatchFailure(message, condition, "条件基础参数无效"); - return false; - } - - // 1.2 检查标识符是否匹配 - String messageIdentifier = IotDeviceMessageUtils.getIdentifier(message); - if (!IotSceneRuleMatcherHelper.isIdentifierMatched(condition.getIdentifier(), messageIdentifier)) { - IotSceneRuleMatcherHelper.logConditionMatchFailure(message, condition, "标识符不匹配,期望: " + condition.getIdentifier() + ", 实际: " + messageIdentifier); - return false; - } - - // 1.3 检查操作符和参数是否有效 - if (!IotSceneRuleMatcherHelper.isConditionOperatorAndParamValid(condition)) { - IotSceneRuleMatcherHelper.logConditionMatchFailure(message, condition, "操作符或参数无效"); - return false; - } - - // 2.1. 获取属性值 - 使用工具类方法正确提取属性值 - Object propertyValue = IotDeviceMessageUtils.extractPropertyValue(message, condition.getIdentifier()); - if (propertyValue == null) { - IotSceneRuleMatcherHelper.logConditionMatchFailure(message, condition, "消息中属性值为空或未找到指定属性"); - return false; - } - - // 2.2 使用条件评估器进行匹配 - boolean matched = IotSceneRuleMatcherHelper.evaluateCondition(propertyValue, condition.getOperator(), condition.getParam()); - if (matched) { - IotSceneRuleMatcherHelper.logConditionMatchSuccess(message, condition); - } else { - IotSceneRuleMatcherHelper.logConditionMatchFailure(message, condition, "设备属性条件不匹配"); - } - return matched; - } - - @Override - public int getPriority() { - return 25; // 中等优先级 - } - -} diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/rule/scene/matcher/condition/IotDeviceStateConditionMatcher.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/rule/scene/matcher/condition/IotDeviceStateConditionMatcher.java deleted file mode 100644 index 2328122..0000000 --- a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/rule/scene/matcher/condition/IotDeviceStateConditionMatcher.java +++ /dev/null @@ -1,60 +0,0 @@ -package cn.iocoder.yudao.module.iot.service.rule.scene.matcher.condition; - -import cn.iocoder.yudao.module.iot.core.mq.message.IotDeviceMessage; -import cn.iocoder.yudao.module.iot.core.util.IotDeviceMessageUtils; -import cn.iocoder.yudao.module.iot.dal.dataobject.rule.IotSceneRuleDO; -import cn.iocoder.yudao.module.iot.enums.rule.IotSceneRuleConditionTypeEnum; -import cn.iocoder.yudao.module.iot.service.rule.scene.matcher.IotSceneRuleMatcherHelper; -import org.springframework.stereotype.Component; - -/** - * 设备状态条件匹配器:处理设备状态相关的子条件匹配逻辑 - * - * @author HUIHUI - */ -@Component -public class IotDeviceStateConditionMatcher implements IotSceneRuleConditionMatcher { - - @Override - public IotSceneRuleConditionTypeEnum getSupportedConditionType() { - return IotSceneRuleConditionTypeEnum.DEVICE_STATE; - } - - @Override - public boolean matches(IotDeviceMessage message, IotSceneRuleDO.TriggerCondition condition) { - // 1.1 基础参数校验 - if (!IotSceneRuleMatcherHelper.isBasicConditionValid(condition)) { - IotSceneRuleMatcherHelper.logConditionMatchFailure(message, condition, "条件基础参数无效"); - return false; - } - - // 1.2 检查操作符和参数是否有效 - if (!IotSceneRuleMatcherHelper.isConditionOperatorAndParamValid(condition)) { - IotSceneRuleMatcherHelper.logConditionMatchFailure(message, condition, "操作符或参数无效"); - return false; - } - - // 2.1 获取设备状态值 - 使用工具类方法正确提取状态值 - // 对于设备状态条件,状态值通过 getIdentifier 获取(实际是从 params.state 字段) - String stateValue = IotDeviceMessageUtils.getIdentifier(message); - if (stateValue == null) { - IotSceneRuleMatcherHelper.logConditionMatchFailure(message, condition, "消息中设备状态值为空"); - return false; - } - - // 2.2 使用条件评估器进行匹配 - boolean matched = IotSceneRuleMatcherHelper.evaluateCondition(stateValue, condition.getOperator(), condition.getParam()); - if (matched) { - IotSceneRuleMatcherHelper.logConditionMatchSuccess(message, condition); - } else { - IotSceneRuleMatcherHelper.logConditionMatchFailure(message, condition, "设备状态条件不匹配"); - } - return matched; - } - - @Override - public int getPriority() { - return 30; // 中等优先级 - } - -} diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/rule/scene/matcher/condition/IotSceneRuleConditionMatcher.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/rule/scene/matcher/condition/IotSceneRuleConditionMatcher.java deleted file mode 100644 index c9f720d..0000000 --- a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/rule/scene/matcher/condition/IotSceneRuleConditionMatcher.java +++ /dev/null @@ -1,35 +0,0 @@ -package cn.iocoder.yudao.module.iot.service.rule.scene.matcher.condition; - -import cn.iocoder.yudao.module.iot.core.mq.message.IotDeviceMessage; -import cn.iocoder.yudao.module.iot.dal.dataobject.rule.IotSceneRuleDO; -import cn.iocoder.yudao.module.iot.enums.rule.IotSceneRuleConditionTypeEnum; -import cn.iocoder.yudao.module.iot.service.rule.scene.matcher.IotSceneRuleMatcher; - -/** - * IoT 场景规则条件匹配器接口:专门处理子条件的匹配逻辑,如设备状态、属性值、时间条件等 - * - * 条件匹配器负责判断设备消息是否满足场景规则的附加条件,在触发器匹配成功后进行进一步的条件筛选 - * - * @author HUIHUI - */ -public interface IotSceneRuleConditionMatcher extends IotSceneRuleMatcher { - - /** - * 获取支持的条件类型 - * - * @return 条件类型枚举 - */ - IotSceneRuleConditionTypeEnum getSupportedConditionType(); - - /** - * 检查条件是否匹配消息 - *

- * 判断设备消息是否满足指定的触发条件 - * - * @param message 设备消息 - * @param condition 触发条件 - * @return 是否匹配 - */ - boolean matches(IotDeviceMessage message, IotSceneRuleDO.TriggerCondition condition); - -} diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/rule/scene/matcher/trigger/IotDeviceEventPostTriggerMatcher.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/rule/scene/matcher/trigger/IotDeviceEventPostTriggerMatcher.java deleted file mode 100644 index c8c0883..0000000 --- a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/rule/scene/matcher/trigger/IotDeviceEventPostTriggerMatcher.java +++ /dev/null @@ -1,73 +0,0 @@ -package cn.iocoder.yudao.module.iot.service.rule.scene.matcher.trigger; - -import cn.hutool.core.util.StrUtil; -import cn.iocoder.yudao.module.iot.core.enums.IotDeviceMessageMethodEnum; -import cn.iocoder.yudao.module.iot.core.mq.message.IotDeviceMessage; -import cn.iocoder.yudao.module.iot.core.util.IotDeviceMessageUtils; -import cn.iocoder.yudao.module.iot.dal.dataobject.rule.IotSceneRuleDO; -import cn.iocoder.yudao.module.iot.enums.rule.IotSceneRuleTriggerTypeEnum; -import cn.iocoder.yudao.module.iot.service.rule.scene.matcher.IotSceneRuleMatcherHelper; -import org.springframework.stereotype.Component; - -/** - * 设备事件上报触发器匹配器:处理设备事件上报的触发器匹配逻辑 - * - * @author HUIHUI - */ -@Component -public class IotDeviceEventPostTriggerMatcher implements IotSceneRuleTriggerMatcher { - - @Override - public IotSceneRuleTriggerTypeEnum getSupportedTriggerType() { - return IotSceneRuleTriggerTypeEnum.DEVICE_EVENT_POST; - } - - @Override - public boolean matches(IotDeviceMessage message, IotSceneRuleDO.Trigger trigger) { - // 1.1 基础参数校验 - if (!IotSceneRuleMatcherHelper.isBasicTriggerValid(trigger)) { - IotSceneRuleMatcherHelper.logTriggerMatchFailure(message, trigger, "触发器基础参数无效"); - return false; - } - - // 1.2 检查消息方法是否匹配 - if (!IotDeviceMessageMethodEnum.EVENT_POST.getMethod().equals(message.getMethod())) { - IotSceneRuleMatcherHelper.logTriggerMatchFailure(message, trigger, "消息方法不匹配,期望: " + - IotDeviceMessageMethodEnum.EVENT_POST.getMethod() + ", 实际: " + message.getMethod()); - return false; - } - - // 1.3 检查标识符是否匹配 - String messageIdentifier = IotDeviceMessageUtils.getIdentifier(message); - if (!IotSceneRuleMatcherHelper.isIdentifierMatched(trigger.getIdentifier(), messageIdentifier)) { - IotSceneRuleMatcherHelper.logTriggerMatchFailure(message, trigger, "标识符不匹配,期望: " + - trigger.getIdentifier() + ", 实际: " + messageIdentifier); - return false; - } - - // 2. 对于事件触发器,通常不需要检查操作符和值,只要事件发生即匹配 - // 但如果配置了操作符和值,则需要进行条件匹配 - if (StrUtil.isNotBlank(trigger.getOperator()) && StrUtil.isNotBlank(trigger.getValue())) { - Object eventParams = message.getParams(); - if (eventParams == null) { - IotSceneRuleMatcherHelper.logTriggerMatchFailure(message, trigger, "消息中事件参数为空"); - return false; - } - - boolean matched = IotSceneRuleMatcherHelper.evaluateCondition(eventParams, trigger.getOperator(), trigger.getValue()); - if (!matched) { - IotSceneRuleMatcherHelper.logTriggerMatchFailure(message, trigger, "事件数据条件不匹配"); - return false; - } - } - - IotSceneRuleMatcherHelper.logTriggerMatchSuccess(message, trigger); - return true; - } - - @Override - public int getPriority() { - return 30; // 中等优先级 - } - -} diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/rule/scene/matcher/trigger/IotDevicePropertyPostTriggerMatcher.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/rule/scene/matcher/trigger/IotDevicePropertyPostTriggerMatcher.java deleted file mode 100644 index 1f019b5..0000000 --- a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/rule/scene/matcher/trigger/IotDevicePropertyPostTriggerMatcher.java +++ /dev/null @@ -1,75 +0,0 @@ -package cn.iocoder.yudao.module.iot.service.rule.scene.matcher.trigger; - -import cn.iocoder.yudao.module.iot.core.enums.IotDeviceMessageMethodEnum; -import cn.iocoder.yudao.module.iot.core.mq.message.IotDeviceMessage; -import cn.iocoder.yudao.module.iot.core.util.IotDeviceMessageUtils; -import cn.iocoder.yudao.module.iot.dal.dataobject.rule.IotSceneRuleDO; -import cn.iocoder.yudao.module.iot.enums.rule.IotSceneRuleTriggerTypeEnum; -import cn.iocoder.yudao.module.iot.service.rule.scene.matcher.IotSceneRuleMatcherHelper; -import org.springframework.stereotype.Component; - -/** - * 设备属性上报触发器匹配器:处理设备属性数据上报的触发器匹配逻辑 - * - * @author HUIHUI - */ -@Component -public class IotDevicePropertyPostTriggerMatcher implements IotSceneRuleTriggerMatcher { - - @Override - public IotSceneRuleTriggerTypeEnum getSupportedTriggerType() { - return IotSceneRuleTriggerTypeEnum.DEVICE_PROPERTY_POST; - } - - @Override - public boolean matches(IotDeviceMessage message, IotSceneRuleDO.Trigger trigger) { - // 1.1 基础参数校验 - if (!IotSceneRuleMatcherHelper.isBasicTriggerValid(trigger)) { - IotSceneRuleMatcherHelper.logTriggerMatchFailure(message, trigger, "触发器基础参数无效"); - return false; - } - - // 1.2 检查消息方法是否匹配 - if (!IotDeviceMessageMethodEnum.PROPERTY_POST.getMethod().equals(message.getMethod())) { - IotSceneRuleMatcherHelper.logTriggerMatchFailure(message, trigger, "消息方法不匹配,期望: " + - IotDeviceMessageMethodEnum.PROPERTY_POST.getMethod() + ", 实际: " + message.getMethod()); - return false; - } - - // 1.3 检查消息中是否包含触发器指定的属性标识符 - // 注意:属性上报可能同时上报多个属性,所以需要判断 trigger.getIdentifier() 是否在 message 的 params 中 - if (IotDeviceMessageUtils.notContainsIdentifier(message, trigger.getIdentifier())) { - IotSceneRuleMatcherHelper.logTriggerMatchFailure(message, trigger, "消息中不包含属性: " + - trigger.getIdentifier()); - return false; - } - - // 1.4 检查操作符和值是否有效 - if (!IotSceneRuleMatcherHelper.isTriggerOperatorAndValueValid(trigger)) { - IotSceneRuleMatcherHelper.logTriggerMatchFailure(message, trigger, "操作符或值无效"); - return false; - } - - // 2.1 获取属性值 - 使用工具类方法正确提取属性值 - Object propertyValue = IotDeviceMessageUtils.extractPropertyValue(message, trigger.getIdentifier()); - if (propertyValue == null) { - IotSceneRuleMatcherHelper.logTriggerMatchFailure(message, trigger, "消息中属性值为空或未找到指定属性"); - return false; - } - - // 2.2 使用条件评估器进行匹配 - boolean matched = IotSceneRuleMatcherHelper.evaluateCondition(propertyValue, trigger.getOperator(), trigger.getValue()); - if (matched) { - IotSceneRuleMatcherHelper.logTriggerMatchSuccess(message, trigger); - } else { - IotSceneRuleMatcherHelper.logTriggerMatchFailure(message, trigger, "属性值条件不匹配"); - } - return matched; - } - - @Override - public int getPriority() { - return 20; // 中等优先级 - } - -} diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/rule/scene/matcher/trigger/IotDeviceServiceInvokeTriggerMatcher.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/rule/scene/matcher/trigger/IotDeviceServiceInvokeTriggerMatcher.java deleted file mode 100644 index 642fb5e..0000000 --- a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/rule/scene/matcher/trigger/IotDeviceServiceInvokeTriggerMatcher.java +++ /dev/null @@ -1,103 +0,0 @@ -package cn.iocoder.yudao.module.iot.service.rule.scene.matcher.trigger; - -import cn.hutool.core.util.StrUtil; -import cn.iocoder.yudao.module.iot.core.enums.IotDeviceMessageMethodEnum; -import cn.iocoder.yudao.module.iot.core.mq.message.IotDeviceMessage; -import cn.iocoder.yudao.module.iot.core.util.IotDeviceMessageUtils; -import cn.iocoder.yudao.module.iot.dal.dataobject.rule.IotSceneRuleDO; -import cn.iocoder.yudao.module.iot.enums.rule.IotSceneRuleTriggerTypeEnum; -import cn.iocoder.yudao.module.iot.service.rule.scene.matcher.IotSceneRuleMatcherHelper; -import org.springframework.stereotype.Component; - -import java.util.Map; - -/** - * 设备服务调用触发器匹配器:处理设备服务调用的触发器匹配逻辑 - * - * @author HUIHUI - */ -@Component -public class IotDeviceServiceInvokeTriggerMatcher implements IotSceneRuleTriggerMatcher { - - @Override - public IotSceneRuleTriggerTypeEnum getSupportedTriggerType() { - return IotSceneRuleTriggerTypeEnum.DEVICE_SERVICE_INVOKE; - } - - @Override - public boolean matches(IotDeviceMessage message, IotSceneRuleDO.Trigger trigger) { - // 1.1 基础参数校验 - if (!IotSceneRuleMatcherHelper.isBasicTriggerValid(trigger)) { - IotSceneRuleMatcherHelper.logTriggerMatchFailure(message, trigger, "触发器基础参数无效"); - return false; - } - // 1.2 检查消息方法是否匹配 - if (!IotDeviceMessageMethodEnum.SERVICE_INVOKE.getMethod().equals(message.getMethod())) { - IotSceneRuleMatcherHelper.logTriggerMatchFailure(message, trigger, "消息方法不匹配,期望: " + IotDeviceMessageMethodEnum.SERVICE_INVOKE.getMethod() + ", 实际: " + message.getMethod()); - return false; - } - // 1.3 检查标识符是否匹配 - String messageIdentifier = IotDeviceMessageUtils.getIdentifier(message); - if (!IotSceneRuleMatcherHelper.isIdentifierMatched(trigger.getIdentifier(), messageIdentifier)) { - IotSceneRuleMatcherHelper.logTriggerMatchFailure(message, trigger, "标识符不匹配,期望: " + trigger.getIdentifier() + ", 实际: " + messageIdentifier); - return false; - } - - // 2. 检查是否配置了参数条件 - if (hasParameterCondition(trigger)) { - return matchParameterCondition(message, trigger); - } - - // 3. 无参数条件时,标识符匹配即成功 - IotSceneRuleMatcherHelper.logTriggerMatchSuccess(message, trigger); - return true; - } - - /** - * 判断触发器是否配置了参数条件 - * - * @param trigger 触发器配置 - * @return 是否配置了参数条件 - */ - private boolean hasParameterCondition(IotSceneRuleDO.Trigger trigger) { - return StrUtil.isNotBlank(trigger.getOperator()) && StrUtil.isNotBlank(trigger.getValue()); - } - - /** - * 匹配参数条件 - * - * @param message 设备消息 - * @param trigger 触发器配置 - * @return 是否匹配 - */ - private boolean matchParameterCondition(IotDeviceMessage message, IotSceneRuleDO.Trigger trigger) { - // 1.1 从消息中提取服务调用的输入参数 - Map inputParams = IotDeviceMessageUtils.extractServiceInputParams(message); - // TODO @puhui999:要考虑 empty 的情况么? - if (inputParams == null) { - IotSceneRuleMatcherHelper.logTriggerMatchFailure(message, trigger, "消息中缺少服务输入参数"); - return false; - } - // 1.2 获取要匹配的参数值(使用 identifier 作为参数名) - Object paramValue = inputParams.get(trigger.getIdentifier()); - if (paramValue == null) { - IotSceneRuleMatcherHelper.logTriggerMatchFailure(message, trigger, "服务输入参数中缺少指定参数: " + trigger.getIdentifier()); - return false; - } - - // 2. 使用条件评估器进行匹配 - boolean matched = IotSceneRuleMatcherHelper.evaluateCondition(paramValue, trigger.getOperator(), trigger.getValue()); - if (matched) { - IotSceneRuleMatcherHelper.logTriggerMatchSuccess(message, trigger); - } else { - IotSceneRuleMatcherHelper.logTriggerMatchFailure(message, trigger, "服务输入参数条件不匹配"); - } - return matched; - } - - @Override - public int getPriority() { - return 40; // 较低优先级 - } - -} diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/rule/scene/matcher/trigger/IotDeviceStateUpdateTriggerMatcher.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/rule/scene/matcher/trigger/IotDeviceStateUpdateTriggerMatcher.java deleted file mode 100644 index 6b8c73a..0000000 --- a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/rule/scene/matcher/trigger/IotDeviceStateUpdateTriggerMatcher.java +++ /dev/null @@ -1,69 +0,0 @@ -package cn.iocoder.yudao.module.iot.service.rule.scene.matcher.trigger; - -import cn.iocoder.yudao.module.iot.core.enums.IotDeviceMessageMethodEnum; -import cn.iocoder.yudao.module.iot.core.mq.message.IotDeviceMessage; -import cn.iocoder.yudao.module.iot.core.util.IotDeviceMessageUtils; -import cn.iocoder.yudao.module.iot.dal.dataobject.rule.IotSceneRuleDO; -import cn.iocoder.yudao.module.iot.enums.rule.IotSceneRuleTriggerTypeEnum; -import cn.iocoder.yudao.module.iot.service.rule.scene.matcher.IotSceneRuleMatcherHelper; -import org.springframework.stereotype.Component; - -/** - * 设备状态更新触发器匹配器:处理设备上下线状态变更的触发器匹配逻辑 - * - * @author HUIHUI - */ -@Component -public class IotDeviceStateUpdateTriggerMatcher implements IotSceneRuleTriggerMatcher { - - @Override - public IotSceneRuleTriggerTypeEnum getSupportedTriggerType() { - return IotSceneRuleTriggerTypeEnum.DEVICE_STATE_UPDATE; - } - - @Override - public boolean matches(IotDeviceMessage message, IotSceneRuleDO.Trigger trigger) { - // 1.1 基础参数校验 - if (!IotSceneRuleMatcherHelper.isBasicTriggerValid(trigger)) { - IotSceneRuleMatcherHelper.logTriggerMatchFailure(message, trigger, "触发器基础参数无效"); - return false; - } - - // 1.2 检查消息方法是否匹配 - if (!IotDeviceMessageMethodEnum.STATE_UPDATE.getMethod().equals(message.getMethod())) { - IotSceneRuleMatcherHelper.logTriggerMatchFailure(message, trigger, "消息方法不匹配,期望: " + - IotDeviceMessageMethodEnum.STATE_UPDATE.getMethod() + ", 实际: " + message.getMethod()); - return false; - } - - // 1.3 检查操作符和值是否有效 - if (!IotSceneRuleMatcherHelper.isTriggerOperatorAndValueValid(trigger)) { - IotSceneRuleMatcherHelper.logTriggerMatchFailure(message, trigger, "操作符或值无效"); - return false; - } - - // 2.1 获取设备状态值 - 使用工具类方法正确提取状态值 - // 对于状态更新消息,状态值通过 getIdentifier 获取(实际是从 params.state 字段) - String stateIdentifier = IotDeviceMessageUtils.getIdentifier(message); - if (stateIdentifier == null) { - IotSceneRuleMatcherHelper.logTriggerMatchFailure(message, trigger, "消息中设备状态值为空"); - return false; - } - - // 2.2 使用条件评估器进行匹配 - // 状态值通常是字符串或数字,直接使用标识符作为状态值 - boolean matched = IotSceneRuleMatcherHelper.evaluateCondition(stateIdentifier, trigger.getOperator(), trigger.getValue()); - if (matched) { - IotSceneRuleMatcherHelper.logTriggerMatchSuccess(message, trigger); - } else { - IotSceneRuleMatcherHelper.logTriggerMatchFailure(message, trigger, "状态值条件不匹配"); - } - return matched; - } - - @Override - public int getPriority() { - return 10; // 高优先级 - } - -} diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/rule/scene/matcher/trigger/IotSceneRuleTriggerMatcher.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/rule/scene/matcher/trigger/IotSceneRuleTriggerMatcher.java deleted file mode 100644 index 84ea579..0000000 --- a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/rule/scene/matcher/trigger/IotSceneRuleTriggerMatcher.java +++ /dev/null @@ -1,35 +0,0 @@ -package cn.iocoder.yudao.module.iot.service.rule.scene.matcher.trigger; - -import cn.iocoder.yudao.module.iot.core.mq.message.IotDeviceMessage; -import cn.iocoder.yudao.module.iot.dal.dataobject.rule.IotSceneRuleDO; -import cn.iocoder.yudao.module.iot.enums.rule.IotSceneRuleTriggerTypeEnum; -import cn.iocoder.yudao.module.iot.service.rule.scene.matcher.IotSceneRuleMatcher; - -/** - * IoT 场景规则触发器匹配器接口:专门处理主触发条件的匹配逻辑,如设备消息类型、定时器等 - * - * 触发器匹配器负责判断设备消息是否满足场景规则的主触发条件,是场景规则执行的第一道门槛 - * - * @author HUIHUI - */ -public interface IotSceneRuleTriggerMatcher extends IotSceneRuleMatcher { - - /** - * 获取支持的触发器类型 - * - * @return 触发器类型枚举 - */ - IotSceneRuleTriggerTypeEnum getSupportedTriggerType(); - - /** - * 检查触发器是否匹配消息 - *

- * 判断设备消息是否满足指定的触发器条件 - * - * @param message 设备消息 - * @param trigger 触发器配置 - * @return 是否匹配 - */ - boolean matches(IotDeviceMessage message, IotSceneRuleDO.Trigger trigger); - -} diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/rule/scene/matcher/trigger/IotTimerTriggerMatcher.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/rule/scene/matcher/trigger/IotTimerTriggerMatcher.java deleted file mode 100644 index f980c24..0000000 --- a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/rule/scene/matcher/trigger/IotTimerTriggerMatcher.java +++ /dev/null @@ -1,56 +0,0 @@ -package cn.iocoder.yudao.module.iot.service.rule.scene.matcher.trigger; - -import cn.hutool.core.util.StrUtil; -import cn.iocoder.yudao.module.iot.core.mq.message.IotDeviceMessage; -import cn.iocoder.yudao.module.iot.dal.dataobject.rule.IotSceneRuleDO; -import cn.iocoder.yudao.module.iot.enums.rule.IotSceneRuleTriggerTypeEnum; -import cn.iocoder.yudao.module.iot.service.rule.scene.matcher.IotSceneRuleMatcherHelper; -import org.quartz.CronExpression; -import org.springframework.stereotype.Component; - -/** - * 定时触发器匹配器:处理定时触发的触发器匹配逻辑 - * - * 注意:定时触发器不依赖设备消息,主要用于定时任务场景 - * - * @author HUIHUI - */ -@Component -public class IotTimerTriggerMatcher implements IotSceneRuleTriggerMatcher { - - @Override - public IotSceneRuleTriggerTypeEnum getSupportedTriggerType() { - return IotSceneRuleTriggerTypeEnum.TIMER; - } - - @Override - public boolean matches(IotDeviceMessage message, IotSceneRuleDO.Trigger trigger) { - // 1.1 基础参数校验 - if (!IotSceneRuleMatcherHelper.isBasicTriggerValid(trigger)) { - IotSceneRuleMatcherHelper.logTriggerMatchFailure(message, trigger, "触发器基础参数无效"); - return false; - } - - // 1.2 检查 CRON 表达式是否存在 - if (StrUtil.isBlank(trigger.getCronExpression())) { - IotSceneRuleMatcherHelper.logTriggerMatchFailure(message, trigger, "定时触发器缺少 CRON 表达式"); - return false; - } - - // 1.3 定时触发器通常不依赖具体的设备消息 - // 它是通过定时任务调度器触发的,这里主要是验证配置的有效性 - if (!CronExpression.isValidExpression(trigger.getCronExpression())) { - IotSceneRuleMatcherHelper.logTriggerMatchFailure(message, trigger, "CRON 表达式格式无效: " + trigger.getCronExpression()); - return false; - } - - IotSceneRuleMatcherHelper.logTriggerMatchSuccess(message, trigger); - return true; - } - - @Override - public int getPriority() { - return 50; // 最低优先级,因为定时触发器不依赖消息 - } - -} diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/rule/scene/timer/IotSceneRuleTimerHandler.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/rule/scene/timer/IotSceneRuleTimerHandler.java deleted file mode 100644 index d27ed66..0000000 --- a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/rule/scene/timer/IotSceneRuleTimerHandler.java +++ /dev/null @@ -1,154 +0,0 @@ -package cn.iocoder.yudao.module.iot.service.rule.scene.timer; - -import cn.hutool.core.collection.CollUtil; -import cn.hutool.core.util.ObjUtil; -import cn.hutool.core.util.StrUtil; -import cn.iocoder.yudao.framework.common.enums.CommonStatusEnum; -import cn.iocoder.yudao.module.iot.dal.dataobject.rule.IotSceneRuleDO; -import cn.iocoder.yudao.module.iot.enums.rule.IotSceneRuleTriggerTypeEnum; -import cn.iocoder.yudao.module.iot.framework.job.core.IotSchedulerManager; -import cn.iocoder.yudao.module.iot.job.rule.IotSceneRuleJob; -import lombok.extern.slf4j.Slf4j; -import org.quartz.SchedulerException; -import org.springframework.stereotype.Component; - -import javax.annotation.Resource; -import java.util.List; - -import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.filterList; - -/** - * IoT 场景规则定时触发器处理器:负责管理定时触发器的注册、更新、删除等操作 - * - * @author HUIHUI - */ -@Component -@Slf4j -public class IotSceneRuleTimerHandler { - - @Resource(name = "iotSchedulerManager") - private IotSchedulerManager schedulerManager; - - /** - * 注册场景规则的定时触发器 - * - * @param sceneRule 场景规则 - */ - public void registerTimerTriggers(IotSceneRuleDO sceneRule) { - // 1. 过滤出定时触发器 - if (sceneRule == null || CollUtil.isEmpty(sceneRule.getTriggers())) { - return; - } - List timerTriggers = filterList(sceneRule.getTriggers(), - trigger -> ObjUtil.equals(trigger.getType(), IotSceneRuleTriggerTypeEnum.TIMER.getType())); - if (CollUtil.isEmpty(timerTriggers)) { - return; - } - - // 2. 注册每个定时触发器 - timerTriggers.forEach(trigger -> registerSingleTimerTrigger(sceneRule, trigger)); - } - - /** - * 更新场景规则的定时触发器 - * - * @param sceneRule 场景规则 - */ - public void updateTimerTriggers(IotSceneRuleDO sceneRule) { - if (sceneRule == null) { - return; - } - - // 1. 先删除旧的定时任务 - unregisterTimerTriggers(sceneRule.getId()); - - // 2.1 如果场景规则已禁用,则不重新注册 - if (CommonStatusEnum.isDisable(sceneRule.getStatus())) { - log.info("[updateTimerTriggers][场景规则({}) 已禁用,不注册定时触发器]", sceneRule.getId()); - return; - } - - // 2.2 重新注册定时触发器 - registerTimerTriggers(sceneRule); - } - - /** - * 注销场景规则的定时触发器 - * - * @param sceneRuleId 场景规则 ID - */ - public void unregisterTimerTriggers(Long sceneRuleId) { - if (sceneRuleId == null) { - return; - } - - String jobName = buildJobName(sceneRuleId); - try { - schedulerManager.deleteJob(jobName); - log.info("[unregisterTimerTriggers][场景规则({}) 定时触发器注销成功]", sceneRuleId); - } catch (SchedulerException e) { - log.error("[unregisterTimerTriggers][场景规则({}) 定时触发器注销失败]", sceneRuleId, e); - } - } - - /** - * 暂停场景规则的定时触发器 - * - * @param sceneRuleId 场景规则 ID - */ - public void pauseTimerTriggers(Long sceneRuleId) { - if (sceneRuleId == null) { - return; - } - - String jobName = buildJobName(sceneRuleId); - try { - schedulerManager.pauseJob(jobName); - log.info("[pauseTimerTriggers][场景规则({}) 定时触发器暂停成功]", sceneRuleId); - } catch (SchedulerException e) { - log.error("[pauseTimerTriggers][场景规则({}) 定时触发器暂停失败]", sceneRuleId, e); - } - } - - /** - * 注册单个定时触发器 - * - * @param sceneRule 场景规则 - * @param trigger 定时触发器配置 - */ - private void registerSingleTimerTrigger(IotSceneRuleDO sceneRule, IotSceneRuleDO.Trigger trigger) { - // 1. 参数校验 - if (StrUtil.isBlank(trigger.getCronExpression())) { - log.error("[registerSingleTimerTrigger][场景规则({}) 定时触发器缺少 CRON 表达式]", sceneRule.getId()); - return; - } - - try { - // 2.1 构建任务名称和数据 - String jobName = buildJobName(sceneRule.getId()); - // 2.2 注册定时任务 - schedulerManager.addOrUpdateJob( - IotSceneRuleJob.class, - jobName, - trigger.getCronExpression(), - IotSceneRuleJob.buildJobDataMap(sceneRule.getId()) - ); - log.info("[registerSingleTimerTrigger][场景规则({}) 定时触发器注册成功,CRON: {}]", - sceneRule.getId(), trigger.getCronExpression()); - } catch (SchedulerException e) { - log.error("[registerSingleTimerTrigger][场景规则({}) 定时触发器注册失败,CRON: {}]", - sceneRule.getId(), trigger.getCronExpression(), e); - } - } - - /** - * 构建任务名称 - * - * @param sceneRuleId 场景规则 ID - * @return 任务名称 - */ - private String buildJobName(Long sceneRuleId) { - return "iot_scene_rule_timer_" + sceneRuleId; - } - -} diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/rule/scene/timer/IotTimerConditionEvaluator.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/rule/scene/timer/IotTimerConditionEvaluator.java deleted file mode 100644 index 2ff0417..0000000 --- a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/rule/scene/timer/IotTimerConditionEvaluator.java +++ /dev/null @@ -1,187 +0,0 @@ -package cn.iocoder.yudao.module.iot.service.rule.scene.timer; - -import cn.hutool.core.collection.CollUtil; -import cn.hutool.core.util.StrUtil; -import cn.iocoder.yudao.module.iot.dal.dataobject.device.IotDeviceDO; -import cn.iocoder.yudao.module.iot.dal.dataobject.device.IotDevicePropertyDO; -import cn.iocoder.yudao.module.iot.dal.dataobject.rule.IotSceneRuleDO; -import cn.iocoder.yudao.module.iot.enums.rule.IotSceneRuleConditionOperatorEnum; -import cn.iocoder.yudao.module.iot.enums.rule.IotSceneRuleConditionTypeEnum; -import cn.iocoder.yudao.module.iot.service.device.IotDeviceService; -import cn.iocoder.yudao.module.iot.service.device.property.IotDevicePropertyService; -import cn.iocoder.yudao.module.iot.service.rule.scene.IotSceneRuleTimeHelper; -import cn.iocoder.yudao.module.iot.service.rule.scene.matcher.IotSceneRuleMatcherHelper; -import lombok.extern.slf4j.Slf4j; -import org.springframework.stereotype.Component; - -import javax.annotation.Resource; -import java.util.Map; - -/** - * IoT 定时触发器条件评估器 - *

- * 与设备触发器不同,定时触发器没有设备消息上下文, - * 需要主动查询设备属性和状态来评估条件。 - * - * @author HUIHUI - */ -@Component -@Slf4j -public class IotTimerConditionEvaluator { - - @Resource - private IotDevicePropertyService devicePropertyService; - - @Resource - private IotDeviceService deviceService; - - /** - * 评估条件 - * - * @param condition 条件配置 - * @return 是否满足条件 - */ - @SuppressWarnings("EnhancedSwitchMigration") - public boolean evaluate(IotSceneRuleDO.TriggerCondition condition) { - // 1.1 基础参数校验 - if (condition == null || condition.getType() == null) { - log.warn("[evaluate][条件为空或类型为空]"); - return false; - } - // 1.2 根据条件类型分发到具体的评估方法 - IotSceneRuleConditionTypeEnum conditionType = - IotSceneRuleConditionTypeEnum.typeOf(condition.getType()); - if (conditionType == null) { - log.warn("[evaluate][未知的条件类型: {}]", condition.getType()); - return false; - } - - // 2. 分发评估 - switch (conditionType) { - case DEVICE_PROPERTY: - return evaluateDevicePropertyCondition(condition); - case DEVICE_STATE: - return evaluateDeviceStateCondition(condition); - case CURRENT_TIME: - return evaluateCurrentTimeCondition(condition); - default: - log.warn("[evaluate][未知的条件类型: {}]", conditionType); - return false; - } - } - - /** - * 评估设备属性条件 - * - * @param condition 条件配置 - * @return 是否满足条件 - */ - private boolean evaluateDevicePropertyCondition(IotSceneRuleDO.TriggerCondition condition) { - // 1. 校验必要参数 - if (condition.getDeviceId() == null) { - log.debug("[evaluateDevicePropertyCondition][设备ID为空]"); - return false; - } - if (StrUtil.isBlank(condition.getIdentifier())) { - log.debug("[evaluateDevicePropertyCondition][属性标识符为空]"); - return false; - } - if (!IotSceneRuleMatcherHelper.isConditionOperatorAndParamValid(condition)) { - log.debug("[evaluateDevicePropertyCondition][操作符或参数无效]"); - return false; - } - - // 2.1 获取设备最新属性值 - Map properties = - devicePropertyService.getLatestDeviceProperties(condition.getDeviceId()); - if (CollUtil.isEmpty(properties)) { - log.debug("[evaluateDevicePropertyCondition][设备({}) 无属性数据]", condition.getDeviceId()); - return false; - } - // 2.2 获取指定属性 - IotDevicePropertyDO property = properties.get(condition.getIdentifier()); - if (property == null || property.getValue() == null) { - log.debug("[evaluateDevicePropertyCondition][设备({}) 属性({}) 不存在或值为空]", - condition.getDeviceId(), condition.getIdentifier()); - return false; - } - - // 3. 使用现有的条件评估逻辑进行比较 - boolean matched = IotSceneRuleMatcherHelper.evaluateCondition( - property.getValue(), condition.getOperator(), condition.getParam()); - log.debug("[evaluateDevicePropertyCondition][设备({}) 属性({}) 值({}) 操作符({}) 参数({}) 匹配结果: {}]", - condition.getDeviceId(), condition.getIdentifier(), property.getValue(), - condition.getOperator(), condition.getParam(), matched); - return matched; - } - - /** - * 评估设备状态条件 - * - * @param condition 条件配置 - * @return 是否满足条件 - */ - private boolean evaluateDeviceStateCondition(IotSceneRuleDO.TriggerCondition condition) { - // 1. 校验必要参数 - if (condition.getDeviceId() == null) { - log.debug("[evaluateDeviceStateCondition][设备ID为空]"); - return false; - } - if (!IotSceneRuleMatcherHelper.isConditionOperatorAndParamValid(condition)) { - log.debug("[evaluateDeviceStateCondition][操作符或参数无效]"); - return false; - } - - // 2.1 获取设备信息 - IotDeviceDO device = deviceService.getDevice(condition.getDeviceId()); - if (device == null) { - log.debug("[evaluateDeviceStateCondition][设备({}) 不存在]", condition.getDeviceId()); - return false; - } - // 2.2 获取设备状态 - Integer state = device.getState(); - if (state == null) { - log.debug("[evaluateDeviceStateCondition][设备({}) 状态为空]", condition.getDeviceId()); - return false; - } - - // 3. 比较状态 - boolean matched = IotSceneRuleMatcherHelper.evaluateCondition( - state.toString(), condition.getOperator(), condition.getParam()); - log.debug("[evaluateDeviceStateCondition][设备({}) 状态({}) 操作符({}) 参数({}) 匹配结果: {}]", - condition.getDeviceId(), state, condition.getOperator(), condition.getParam(), matched); - return matched; - } - - /** - * 评估当前时间条件 - * - * @param condition 条件配置 - * @return 是否满足条件 - */ - private boolean evaluateCurrentTimeCondition(IotSceneRuleDO.TriggerCondition condition) { - // 1.1 校验必要参数 - if (!IotSceneRuleMatcherHelper.isConditionOperatorAndParamValid(condition)) { - log.debug("[evaluateCurrentTimeCondition][操作符或参数无效]"); - return false; - } - // 1.2 验证操作符是否为支持的时间操作符 - IotSceneRuleConditionOperatorEnum operatorEnum = - IotSceneRuleConditionOperatorEnum.operatorOf(condition.getOperator()); - if (operatorEnum == null) { - log.debug("[evaluateCurrentTimeCondition][无效的操作符: {}]", condition.getOperator()); - return false; - } - if (IotSceneRuleTimeHelper.isTimeOperator(operatorEnum)) { - log.debug("[evaluateCurrentTimeCondition][不支持的时间操作符: {}]", condition.getOperator()); - return false; - } - - // 2. 执行时间匹配 - boolean matched = IotSceneRuleTimeHelper.executeTimeMatching(operatorEnum, condition.getParam()); - log.debug("[evaluateCurrentTimeCondition][操作符({}) 参数({}) 匹配结果: {}]", - condition.getOperator(), condition.getParam(), matched); - return matched; - } - -} diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/thingmodel/IotThingModelService.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/thingmodel/IotThingModelService.java deleted file mode 100644 index 9d3aba3..0000000 --- a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/thingmodel/IotThingModelService.java +++ /dev/null @@ -1,111 +0,0 @@ -package cn.iocoder.yudao.module.iot.service.thingmodel; - -import cn.iocoder.yudao.framework.common.pojo.PageResult; -import cn.iocoder.yudao.module.iot.controller.admin.thingmodel.vo.IotThingModelListReqVO; -import cn.iocoder.yudao.module.iot.controller.admin.thingmodel.vo.IotThingModelPageReqVO; -import cn.iocoder.yudao.module.iot.controller.admin.thingmodel.vo.IotThingModelSaveReqVO; -import cn.iocoder.yudao.module.iot.dal.dataobject.thingmodel.IotThingModelDO; - -import javax.validation.Valid; -import java.util.Collection; -import java.util.List; -import java.util.Set; - -/** - * IoT 产品物模型 Service 接口 - * - * @author 芋道源码 - */ -public interface IotThingModelService { - - /** - * 创建产品物模型 - * - * @param createReqVO 创建信息 - * @return 编号 - */ - Long createThingModel(@Valid IotThingModelSaveReqVO createReqVO); - - /** - * 更新产品物模型 - * - * @param updateReqVO 更新信息 - */ - void updateThingModel(@Valid IotThingModelSaveReqVO updateReqVO); - - /** - * 删除产品物模型 - * - * @param id 编号 - */ - void deleteThingModel(Long id); - - /** - * 获得产品物模型 - * - * @param id 编号 - * @return 产品物模型 - */ - IotThingModelDO getThingModel(Long id); - - /** - * 获得产品物模型列表 - * - * @param productId 产品编号 - * @return 产品物模型列表 - */ - List getThingModelListByProductId(Long productId); - - /** - * 获得产品物模型列表 - * - * @param productId 产品编号 - * @param identifiers 功能标识列表 - * @return 产品物模型列表 - */ - List getThingModelListByProductIdAndIdentifiers(Long productId, Collection identifiers); - - /** - * 获得产品物模型列表 - * - * @param productId 产品编号 - * @param type 物模型类型 - * @return 产品物模型列表 - */ - List getThingModelListByProductIdAndType(Long productId, Integer type); - - /** - * 【缓存】获得产品物模型列表 - * - * 注意:该方法会忽略租户信息,所以调用时,需要确认会不会有跨租户访问的风险!!! - * - * @param productId 产品编号 - * @return 产品物模型列表 - */ - List getThingModelListByProductIdFromCache(Long productId); - - /** - * 获得产品物模型分页 - * - * @param pageReqVO 分页查询 - * @return 产品物模型分页 - */ - PageResult getProductThingModelPage(IotThingModelPageReqVO pageReqVO); - - /** - * 获得产品物模型列表 - * - * @param reqVO 列表查询 - * @return 产品物模型列表 - */ - List getThingModelList(IotThingModelListReqVO reqVO); - - /** - * 批量校验物模型存在 - * - * @param productId 产品编号 - * @param identifiers 标识符集合 - */ - void validateThingModelListExists(Long productId, Set identifiers); - -} \ No newline at end of file diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/thingmodel/IotThingModelServiceImpl.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/thingmodel/IotThingModelServiceImpl.java deleted file mode 100644 index f08f9d9..0000000 --- a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/thingmodel/IotThingModelServiceImpl.java +++ /dev/null @@ -1,225 +0,0 @@ -package cn.iocoder.yudao.module.iot.service.thingmodel; - -import cn.hutool.core.collection.CollUtil; -import cn.hutool.core.util.ObjectUtil; -import cn.hutool.core.util.StrUtil; -import cn.hutool.extra.spring.SpringUtil; -import cn.iocoder.yudao.framework.common.pojo.PageResult; -import cn.iocoder.yudao.framework.tenant.core.aop.TenantIgnore; -import cn.iocoder.yudao.module.iot.controller.admin.thingmodel.vo.IotThingModelListReqVO; -import cn.iocoder.yudao.module.iot.controller.admin.thingmodel.vo.IotThingModelPageReqVO; -import cn.iocoder.yudao.module.iot.controller.admin.thingmodel.vo.IotThingModelSaveReqVO; -import cn.iocoder.yudao.module.iot.convert.thingmodel.IotThingModelConvert; -import cn.iocoder.yudao.module.iot.dal.dataobject.product.IotProductDO; -import cn.iocoder.yudao.module.iot.dal.dataobject.thingmodel.IotThingModelDO; -import cn.iocoder.yudao.module.iot.dal.mysql.thingmodel.IotThingModelMapper; -import cn.iocoder.yudao.module.iot.dal.redis.RedisKeyConstants; -import cn.iocoder.yudao.module.iot.enums.product.IotProductStatusEnum; -import cn.iocoder.yudao.module.iot.service.device.IotDeviceModbusPointService; -import cn.iocoder.yudao.module.iot.service.product.IotProductService; -import lombok.extern.slf4j.Slf4j; -import org.springframework.cache.annotation.CacheEvict; -import org.springframework.cache.annotation.Cacheable; -import org.springframework.context.annotation.Lazy; -import org.springframework.stereotype.Service; -import org.springframework.transaction.annotation.Transactional; -import org.springframework.validation.annotation.Validated; - -import javax.annotation.Resource; -import java.util.Collection; -import java.util.List; -import java.util.Objects; -import java.util.Set; - -import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception; -import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertSet; -import static cn.iocoder.yudao.module.iot.enums.ErrorCodeConstants.*; - -/** - * IoT 产品物模型 Service 实现类 - * - * @author 芋道源码 - */ -@Service -@Validated -@Slf4j -public class IotThingModelServiceImpl implements IotThingModelService { - - @Resource - private IotThingModelMapper thingModelMapper; - - @Resource - @Lazy // 延迟加载,解决循环依赖 - private IotProductService productService; - @Resource - @Lazy // 延迟加载,解决循环依赖 - private IotDeviceModbusPointService deviceModbusPointService; - - @Override - @Transactional(rollbackFor = Exception.class) - public Long createThingModel(IotThingModelSaveReqVO createReqVO) { - // 1.1 校验功能标识符在同一产品下是否唯一 - validateIdentifierUnique(null, createReqVO.getProductId(), createReqVO.getIdentifier()); - // 1.2 功能名称在同一产品下是否唯一 - validateNameUnique(createReqVO.getProductId(), createReqVO.getName()); - // 1.3 校验产品状态,发布状态下,不允许新增功能 - validateProductStatus(createReqVO.getProductId()); - - // 2. 插入数据库 - IotThingModelDO thingModel = IotThingModelConvert.INSTANCE.convert(createReqVO); - thingModelMapper.insert(thingModel); - - // 3. 删除缓存 - deleteThingModelListCache(createReqVO.getProductId()); - return thingModel.getId(); - } - - @Override - @Transactional(rollbackFor = Exception.class) - public void updateThingModel(IotThingModelSaveReqVO updateReqVO) { - // 1.1 校验功能是否存在 - validateProductThingModelMapperExists(updateReqVO.getId()); - // 1.2 校验功能标识符是否唯一 - validateIdentifierUnique(updateReqVO.getId(), updateReqVO.getProductId(), updateReqVO.getIdentifier()); - // 1.3 校验产品状态,发布状态下,不允许操作功能 - validateProductStatus(updateReqVO.getProductId()); - - // 2. 更新数据库 - IotThingModelDO thingModel = IotThingModelConvert.INSTANCE.convert(updateReqVO); - thingModelMapper.updateById(thingModel); - - // 3. 同步更新 Modbus 点位的冗余字段(identifier、name) - deviceModbusPointService.updateDeviceModbusPointByThingModel( - updateReqVO.getId(), updateReqVO.getIdentifier(), updateReqVO.getName()); - - // 4. 删除缓存 - deleteThingModelListCache(updateReqVO.getProductId()); - } - - @Override - @Transactional(rollbackFor = Exception.class) - public void deleteThingModel(Long id) { - // 1.1 校验功能是否存在 - IotThingModelDO thingModel = thingModelMapper.selectById(id); - if (thingModel == null) { - throw exception(THING_MODEL_NOT_EXISTS); - } - // 1.2 校验产品状态,发布状态下,不允许操作功能 - validateProductStatus(thingModel.getProductId()); - - // 2. 删除功能 - thingModelMapper.deleteById(id); - - // 3. 删除缓存 - deleteThingModelListCache(thingModel.getProductId()); - } - - @Override - public IotThingModelDO getThingModel(Long id) { - return thingModelMapper.selectById(id); - } - - @Override - public List getThingModelListByProductId(Long productId) { - return thingModelMapper.selectListByProductId(productId); - } - - @Override - public List getThingModelListByProductIdAndIdentifiers(Long productId, Collection identifiers) { - return thingModelMapper.selectListByProductIdAndIdentifiers(productId, identifiers); - } - - @Override - public List getThingModelListByProductIdAndType(Long productId, Integer type) { - return thingModelMapper.selectListByProductIdAndType(productId, type); - } - - @Override - @Cacheable(value = RedisKeyConstants.THING_MODEL_LIST, key = "#productId") - @TenantIgnore // 忽略租户信息 - public List getThingModelListByProductIdFromCache(Long productId) { - return thingModelMapper.selectListByProductId(productId); - } - - @Override - public PageResult getProductThingModelPage(IotThingModelPageReqVO pageReqVO) { - return thingModelMapper.selectPage(pageReqVO); - } - - @Override - public List getThingModelList(IotThingModelListReqVO reqVO) { - return thingModelMapper.selectList(reqVO); - } - - @Override - public void validateThingModelListExists(Long productId, Set identifiers) { - if (CollUtil.isEmpty(identifiers)) { - return; - } - List thingModels = thingModelMapper.selectListByProductIdAndIdentifiers( - productId, identifiers); - Set foundIdentifiers = convertSet(thingModels, IotThingModelDO::getIdentifier); - for (String identifier : identifiers) { - if (!foundIdentifiers.contains(identifier)) { - throw exception(THING_MODEL_NOT_EXISTS); - } - } - } - - private void validateProductThingModelMapperExists(Long id) { - if (thingModelMapper.selectById(id) == null) { - throw exception(THING_MODEL_NOT_EXISTS); - } - } - - private void validateIdentifierUnique(Long id, Long productId, String identifier) { - // 1. 情况一:创建时校验 - if (id == null) { - // 1.1 系统保留字段,不能用于标识符定义 - if (StrUtil.equalsAny(identifier, "set", "get", "post", "property", "event", "time", "value")) { - throw exception(THING_MODEL_IDENTIFIER_INVALID); - } - // 1.2 校验唯一 - IotThingModelDO thingModel = thingModelMapper.selectByProductIdAndIdentifier(productId, identifier); - if (thingModel != null) { - throw exception(THING_MODEL_IDENTIFIER_EXISTS); - } - return; - } - - // 2. 情况二:更新时校验 - IotThingModelDO thingModel = thingModelMapper.selectByProductIdAndIdentifier(productId, identifier); - if (thingModel != null && ObjectUtil.notEqual(thingModel.getId(), id)) { - throw exception(THING_MODEL_IDENTIFIER_EXISTS); - } - } - - private void validateProductStatus(Long createReqVO) { - IotProductDO product = productService.validateProductExists(createReqVO); - if (Objects.equals(product.getStatus(), IotProductStatusEnum.PUBLISHED.getStatus())) { - throw exception(PRODUCT_STATUS_NOT_ALLOW_THING_MODEL); - } - } - - private void validateNameUnique(Long productId, String name) { - IotThingModelDO thingModel = thingModelMapper.selectByProductIdAndName(productId, name); - if (thingModel != null) { - throw exception(THING_MODEL_NAME_EXISTS); - } - } - - private void deleteThingModelListCache(Long productId) { - // 保证 Spring AOP 触发 - getSelf().deleteThingModelListCache0(productId); - } - - @CacheEvict(value = RedisKeyConstants.THING_MODEL_LIST, key = "#productId") - @TenantIgnore // 忽略租户信息 - public void deleteThingModelListCache0(Long productId) { - } - - private IotThingModelServiceImpl getSelf() { - return SpringUtil.getBean(getClass()); - } - -} diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/resources/mapper/device/DeviceMapper.xml b/yudao-module-iot/yudao-module-iot-biz/src/main/resources/mapper/device/DeviceMapper.xml new file mode 100644 index 0000000..f714ceb --- /dev/null +++ b/yudao-module-iot/yudao-module-iot-biz/src/main/resources/mapper/device/DeviceMapper.xml @@ -0,0 +1,207 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/resources/mapper/device/IotDeviceMessageMapper.xml b/yudao-module-iot/yudao-module-iot-biz/src/main/resources/mapper/device/IotDeviceMessageMapper.xml deleted file mode 100644 index deef23b..0000000 --- a/yudao-module-iot/yudao-module-iot-biz/src/main/resources/mapper/device/IotDeviceMessageMapper.xml +++ /dev/null @@ -1,109 +0,0 @@ - - - - - - CREATE STABLE IF NOT EXISTS device_message ( - ts TIMESTAMP, - id NCHAR(50), - report_time TIMESTAMP, - tenant_id BIGINT, - server_id NCHAR(50), - upstream BOOL, - reply BOOL, - identifier NCHAR(100), - request_id NCHAR(50), - method NCHAR(100), - params NCHAR(2048), - data NCHAR(2048), - code INT, - msg NCHAR(256) - ) TAGS ( - device_id BIGINT - ) - - - - - - INSERT INTO device_message_${deviceId} ( - ts, id, report_time, tenant_id, server_id, - upstream, reply, identifier, request_id, method, - params, data, code, msg - ) - USING device_message - TAGS (#{deviceId}) - VALUES ( - NOW, #{id}, #{reportTime}, #{tenantId}, #{serverId}, - #{upstream}, #{reply}, #{identifier}, #{requestId}, #{method}, - #{params}, #{data}, #{code}, #{msg} - ) - - - - - - - - - - - \ No newline at end of file diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/resources/mapper/device/IotDevicePropertyMapper.xml b/yudao-module-iot/yudao-module-iot-biz/src/main/resources/mapper/device/IotDevicePropertyMapper.xml deleted file mode 100644 index 94da3fe..0000000 --- a/yudao-module-iot/yudao-module-iot-biz/src/main/resources/mapper/device/IotDevicePropertyMapper.xml +++ /dev/null @@ -1,79 +0,0 @@ - - - - - - - - CREATE STABLE product_property_${productId} ( - ts TIMESTAMP, - report_time TIMESTAMP, - - ${field.field} ${field.type} - - (${field.length}) - - - ) - TAGS ( - device_id BIGINT - ) - - - - ALTER STABLE product_property_${productId} - ADD COLUMN ${field.field} ${field.type} - - (${field.length}) - - - - - ALTER STABLE product_property_${productId} - MODIFY COLUMN ${field.field} ${field.type} - - (${field.length}) - - - - - ALTER STABLE product_property_${productId} - DROP COLUMN ${field.field} - - - - INSERT INTO device_property_${device.id} - USING product_property_${device.productId} - TAGS ('${device.id}') - (ts, report_time, - - ${@cn.hutool.core.util.StrUtil@toUnderlineCase(key)} - - ) - VALUES - (NOW, #{reportTime}, - - #{value} - - ) - - - - - - - \ No newline at end of file diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/resources/mapper/deviceattributetype/DeviceAttributeTypeMapper.xml b/yudao-module-iot/yudao-module-iot-biz/src/main/resources/mapper/deviceattributetype/DeviceAttributeTypeMapper.xml new file mode 100644 index 0000000..7d7d398 --- /dev/null +++ b/yudao-module-iot/yudao-module-iot-biz/src/main/resources/mapper/deviceattributetype/DeviceAttributeTypeMapper.xml @@ -0,0 +1,12 @@ + + + + + + + \ No newline at end of file diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/resources/mapper/devicecontactmodel/DeviceContactModelMapper.xml b/yudao-module-iot/yudao-module-iot-biz/src/main/resources/mapper/devicecontactmodel/DeviceContactModelMapper.xml new file mode 100644 index 0000000..8f41864 --- /dev/null +++ b/yudao-module-iot/yudao-module-iot-biz/src/main/resources/mapper/devicecontactmodel/DeviceContactModelMapper.xml @@ -0,0 +1,11 @@ + + + + + + + \ No newline at end of file diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/resources/mapper/devicemodel/DeviceModelMapper.xml b/yudao-module-iot/yudao-module-iot-biz/src/main/resources/mapper/devicemodel/DeviceModelMapper.xml new file mode 100644 index 0000000..06f012f --- /dev/null +++ b/yudao-module-iot/yudao-module-iot-biz/src/main/resources/mapper/devicemodel/DeviceModelMapper.xml @@ -0,0 +1,12 @@ + + + + + + + \ No newline at end of file diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/resources/mapper/devicemodelattribute/DeviceModelAttributeMapper.xml b/yudao-module-iot/yudao-module-iot-biz/src/main/resources/mapper/devicemodelattribute/DeviceModelAttributeMapper.xml new file mode 100644 index 0000000..0a68079 --- /dev/null +++ b/yudao-module-iot/yudao-module-iot-biz/src/main/resources/mapper/devicemodelattribute/DeviceModelAttributeMapper.xml @@ -0,0 +1,12 @@ + + + + + + + \ No newline at end of file diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/resources/mapper/devicemodelrules/DeviceModelRulesMapper.xml b/yudao-module-iot/yudao-module-iot-biz/src/main/resources/mapper/devicemodelrules/DeviceModelRulesMapper.xml new file mode 100644 index 0000000..cbe0442 --- /dev/null +++ b/yudao-module-iot/yudao-module-iot-biz/src/main/resources/mapper/devicemodelrules/DeviceModelRulesMapper.xml @@ -0,0 +1,12 @@ + + + + + + + \ No newline at end of file diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/resources/mapper/devicepointrules/DevicePointRulesMapper.xml b/yudao-module-iot/yudao-module-iot-biz/src/main/resources/mapper/devicepointrules/DevicePointRulesMapper.xml new file mode 100644 index 0000000..9dc1f3a --- /dev/null +++ b/yudao-module-iot/yudao-module-iot-biz/src/main/resources/mapper/devicepointrules/DevicePointRulesMapper.xml @@ -0,0 +1,12 @@ + + + + + + + \ No newline at end of file diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/resources/mapper/devicewarinningrecord/DeviceWarinningRecordMapper.xml b/yudao-module-iot/yudao-module-iot-biz/src/main/resources/mapper/devicewarinningrecord/DeviceWarinningRecordMapper.xml new file mode 100644 index 0000000..3e9e34e --- /dev/null +++ b/yudao-module-iot/yudao-module-iot-biz/src/main/resources/mapper/devicewarinningrecord/DeviceWarinningRecordMapper.xml @@ -0,0 +1,22 @@ + + + + + + + + \ No newline at end of file diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/resources/mapper/gateway/GatewayMapper.xml b/yudao-module-iot/yudao-module-iot-biz/src/main/resources/mapper/gateway/GatewayMapper.xml new file mode 100644 index 0000000..a6dd819 --- /dev/null +++ b/yudao-module-iot/yudao-module-iot-biz/src/main/resources/mapper/gateway/GatewayMapper.xml @@ -0,0 +1,12 @@ + + + + + + + \ No newline at end of file diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/resources/mapper/orgnode/OrgNodeMapper.xml b/yudao-module-iot/yudao-module-iot-biz/src/main/resources/mapper/orgnode/OrgNodeMapper.xml new file mode 100644 index 0000000..3380cdc --- /dev/null +++ b/yudao-module-iot/yudao-module-iot-biz/src/main/resources/mapper/orgnode/OrgNodeMapper.xml @@ -0,0 +1,24 @@ + + + + + + + + + + \ No newline at end of file diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/resources/mapper/tdengine/IotDevicePropertyMapper.xml b/yudao-module-iot/yudao-module-iot-biz/src/main/resources/mapper/tdengine/IotDevicePropertyMapper.xml new file mode 100644 index 0000000..47664f0 --- /dev/null +++ b/yudao-module-iot/yudao-module-iot-biz/src/main/resources/mapper/tdengine/IotDevicePropertyMapper.xml @@ -0,0 +1,12 @@ + + + + + + + \ No newline at end of file diff --git a/yudao-module-iot/yudao-module-iot-core/pom.xml b/yudao-module-iot/yudao-module-iot-core/pom.xml index 30ebc2d..69fdfe2 100644 --- a/yudao-module-iot/yudao-module-iot-core/pom.xml +++ b/yudao-module-iot/yudao-module-iot-core/pom.xml @@ -49,6 +49,12 @@ true + + org.eclipse.paho + org.eclipse.paho.client.mqttv3 + 1.2.2 + + org.springframework.amqp spring-rabbit diff --git a/yudao-module-mall/yudao-module-product/src/main/java/cn/iocoder/yudao/module/product/controller/admin/spu/vo/ProductSpuRespVO.java b/yudao-module-mall/yudao-module-product/src/main/java/cn/iocoder/yudao/module/product/controller/admin/spu/vo/ProductSpuRespVO.java index d962fe7..fbc7552 100644 --- a/yudao-module-mall/yudao-module-product/src/main/java/cn/iocoder/yudao/module/product/controller/admin/spu/vo/ProductSpuRespVO.java +++ b/yudao-module-mall/yudao-module-product/src/main/java/cn/iocoder/yudao/module/product/controller/admin/spu/vo/ProductSpuRespVO.java @@ -4,8 +4,8 @@ import cn.iocoder.yudao.framework.excel.core.annotations.DictFormat; import cn.iocoder.yudao.framework.excel.core.convert.DictConvert; import cn.iocoder.yudao.framework.excel.core.convert.MoneyConvert; import cn.iocoder.yudao.module.product.enums.DictTypeConstants; -import cn.idev.excel.annotation.ExcelIgnoreUnannotated; -import cn.idev.excel.annotation.ExcelProperty; +import com.alibaba.excel.annotation.ExcelIgnoreUnannotated; +import com.alibaba.excel.annotation.ExcelProperty; import io.swagger.v3.oas.annotations.media.Schema; import lombok.Data; diff --git a/yudao-module-mall/yudao-module-promotion/src/main/java/cn/iocoder/yudao/module/promotion/controller/admin/point/vo/activity/PointActivityRespVO.java b/yudao-module-mall/yudao-module-promotion/src/main/java/cn/iocoder/yudao/module/promotion/controller/admin/point/vo/activity/PointActivityRespVO.java index 14f1ee4..70ae2e0 100644 --- a/yudao-module-mall/yudao-module-promotion/src/main/java/cn/iocoder/yudao/module/promotion/controller/admin/point/vo/activity/PointActivityRespVO.java +++ b/yudao-module-mall/yudao-module-promotion/src/main/java/cn/iocoder/yudao/module/promotion/controller/admin/point/vo/activity/PointActivityRespVO.java @@ -1,8 +1,8 @@ package cn.iocoder.yudao.module.promotion.controller.admin.point.vo.activity; import cn.iocoder.yudao.module.promotion.controller.admin.point.vo.product.PointProductRespVO; -import cn.idev.excel.annotation.ExcelIgnoreUnannotated; -import cn.idev.excel.annotation.ExcelProperty; +import com.alibaba.excel.annotation.ExcelIgnoreUnannotated; +import com.alibaba.excel.annotation.ExcelProperty; import io.swagger.v3.oas.annotations.media.Schema; import lombok.Data; diff --git a/yudao-module-mall/yudao-module-promotion/src/main/java/cn/iocoder/yudao/module/promotion/controller/app/point/vo/AppPointActivityRespVO.java b/yudao-module-mall/yudao-module-promotion/src/main/java/cn/iocoder/yudao/module/promotion/controller/app/point/vo/AppPointActivityRespVO.java index 83e00c2..ddcc23b 100644 --- a/yudao-module-mall/yudao-module-promotion/src/main/java/cn/iocoder/yudao/module/promotion/controller/app/point/vo/AppPointActivityRespVO.java +++ b/yudao-module-mall/yudao-module-promotion/src/main/java/cn/iocoder/yudao/module/promotion/controller/app/point/vo/AppPointActivityRespVO.java @@ -1,6 +1,6 @@ package cn.iocoder.yudao.module.promotion.controller.app.point.vo; -import cn.idev.excel.annotation.ExcelProperty; +import com.alibaba.excel.annotation.ExcelProperty; import io.swagger.v3.oas.annotations.media.Schema; import lombok.Data; diff --git a/yudao-module-mall/yudao-module-statistics/src/main/java/cn/iocoder/yudao/module/statistics/controller/admin/product/vo/ProductStatisticsRespVO.java b/yudao-module-mall/yudao-module-statistics/src/main/java/cn/iocoder/yudao/module/statistics/controller/admin/product/vo/ProductStatisticsRespVO.java index 86126ca..ab3242f 100644 --- a/yudao-module-mall/yudao-module-statistics/src/main/java/cn/iocoder/yudao/module/statistics/controller/admin/product/vo/ProductStatisticsRespVO.java +++ b/yudao-module-mall/yudao-module-statistics/src/main/java/cn/iocoder/yudao/module/statistics/controller/admin/product/vo/ProductStatisticsRespVO.java @@ -1,7 +1,7 @@ package cn.iocoder.yudao.module.statistics.controller.admin.product.vo; -import cn.idev.excel.annotation.ExcelIgnoreUnannotated; -import cn.idev.excel.annotation.ExcelProperty; +import com.alibaba.excel.annotation.ExcelIgnoreUnannotated; +import com.alibaba.excel.annotation.ExcelProperty; import com.fasterxml.jackson.annotation.JsonFormat; import io.swagger.v3.oas.annotations.media.Schema; import lombok.Data; diff --git a/yudao-module-mall/yudao-module-statistics/src/main/java/cn/iocoder/yudao/module/statistics/controller/admin/trade/vo/TradeTrendSummaryExcelVO.java b/yudao-module-mall/yudao-module-statistics/src/main/java/cn/iocoder/yudao/module/statistics/controller/admin/trade/vo/TradeTrendSummaryExcelVO.java index 9e27c6f..75fa17a 100644 --- a/yudao-module-mall/yudao-module-statistics/src/main/java/cn/iocoder/yudao/module/statistics/controller/admin/trade/vo/TradeTrendSummaryExcelVO.java +++ b/yudao-module-mall/yudao-module-statistics/src/main/java/cn/iocoder/yudao/module/statistics/controller/admin/trade/vo/TradeTrendSummaryExcelVO.java @@ -1,7 +1,7 @@ package cn.iocoder.yudao.module.statistics.controller.admin.trade.vo; import cn.iocoder.yudao.framework.excel.core.convert.MoneyConvert; -import cn.idev.excel.annotation.ExcelProperty; +import com.alibaba.excel.annotation.ExcelProperty; import cn.idev.excel.annotation.format.DateTimeFormat; import lombok.Data; diff --git a/yudao-module-mall/yudao-module-trade/src/main/java/cn/iocoder/yudao/module/trade/controller/admin/delivery/vo/express/DeliveryExpressExcelVO.java b/yudao-module-mall/yudao-module-trade/src/main/java/cn/iocoder/yudao/module/trade/controller/admin/delivery/vo/express/DeliveryExpressExcelVO.java index 3cc312a..c84a3a1 100644 --- a/yudao-module-mall/yudao-module-trade/src/main/java/cn/iocoder/yudao/module/trade/controller/admin/delivery/vo/express/DeliveryExpressExcelVO.java +++ b/yudao-module-mall/yudao-module-trade/src/main/java/cn/iocoder/yudao/module/trade/controller/admin/delivery/vo/express/DeliveryExpressExcelVO.java @@ -3,7 +3,7 @@ package cn.iocoder.yudao.module.trade.controller.admin.delivery.vo.express; import cn.iocoder.yudao.framework.excel.core.annotations.DictFormat; import cn.iocoder.yudao.framework.excel.core.convert.DictConvert; import cn.iocoder.yudao.module.system.enums.DictTypeConstants; -import cn.idev.excel.annotation.ExcelProperty; +import com.alibaba.excel.annotation.ExcelProperty; import lombok.Data; import java.time.LocalDateTime; diff --git a/yudao-module-pay/src/main/java/cn/iocoder/yudao/module/pay/controller/admin/order/vo/PayOrderExcelVO.java b/yudao-module-pay/src/main/java/cn/iocoder/yudao/module/pay/controller/admin/order/vo/PayOrderExcelVO.java index 1cb3625..5dc17a0 100644 --- a/yudao-module-pay/src/main/java/cn/iocoder/yudao/module/pay/controller/admin/order/vo/PayOrderExcelVO.java +++ b/yudao-module-pay/src/main/java/cn/iocoder/yudao/module/pay/controller/admin/order/vo/PayOrderExcelVO.java @@ -4,7 +4,7 @@ import cn.iocoder.yudao.framework.excel.core.annotations.DictFormat; import cn.iocoder.yudao.framework.excel.core.convert.DictConvert; import cn.iocoder.yudao.framework.excel.core.convert.MoneyConvert; import cn.iocoder.yudao.module.pay.enums.DictTypeConstants; -import cn.idev.excel.annotation.ExcelProperty; +import com.alibaba.excel.annotation.ExcelProperty; import lombok.Data; import java.time.LocalDateTime; diff --git a/yudao-module-pay/src/main/java/cn/iocoder/yudao/module/pay/controller/admin/refund/vo/PayRefundExcelVO.java b/yudao-module-pay/src/main/java/cn/iocoder/yudao/module/pay/controller/admin/refund/vo/PayRefundExcelVO.java index 8794086..758b6b6 100644 --- a/yudao-module-pay/src/main/java/cn/iocoder/yudao/module/pay/controller/admin/refund/vo/PayRefundExcelVO.java +++ b/yudao-module-pay/src/main/java/cn/iocoder/yudao/module/pay/controller/admin/refund/vo/PayRefundExcelVO.java @@ -4,7 +4,7 @@ import cn.iocoder.yudao.framework.excel.core.annotations.DictFormat; import cn.iocoder.yudao.framework.excel.core.convert.DictConvert; import cn.iocoder.yudao.framework.excel.core.convert.MoneyConvert; import cn.iocoder.yudao.module.pay.enums.DictTypeConstants; -import cn.idev.excel.annotation.ExcelProperty; +import com.alibaba.excel.annotation.ExcelProperty; import lombok.Data; import java.time.LocalDateTime; diff --git a/yudao-module-pay/src/main/java/cn/iocoder/yudao/module/pay/controller/admin/transfer/vo/PayTransferRespVO.java b/yudao-module-pay/src/main/java/cn/iocoder/yudao/module/pay/controller/admin/transfer/vo/PayTransferRespVO.java index 5c2ac3f..9e98db1 100644 --- a/yudao-module-pay/src/main/java/cn/iocoder/yudao/module/pay/controller/admin/transfer/vo/PayTransferRespVO.java +++ b/yudao-module-pay/src/main/java/cn/iocoder/yudao/module/pay/controller/admin/transfer/vo/PayTransferRespVO.java @@ -5,8 +5,8 @@ import cn.iocoder.yudao.framework.excel.core.convert.DictConvert; import cn.iocoder.yudao.framework.excel.core.convert.MoneyConvert; import cn.iocoder.yudao.module.pay.enums.DictTypeConstants; -import cn.idev.excel.annotation.ExcelIgnoreUnannotated; -import cn.idev.excel.annotation.ExcelProperty; +import com.alibaba.excel.annotation.ExcelIgnoreUnannotated; +import com.alibaba.excel.annotation.ExcelProperty; import io.swagger.v3.oas.annotations.media.Schema; import lombok.*; import java.time.LocalDateTime; diff --git a/yudao-module-system/src/main/java/cn/iocoder/yudao/module/system/controller/admin/dept/vo/post/PostRespVO.java b/yudao-module-system/src/main/java/cn/iocoder/yudao/module/system/controller/admin/dept/vo/post/PostRespVO.java index 18ccb7f..dde6f95 100644 --- a/yudao-module-system/src/main/java/cn/iocoder/yudao/module/system/controller/admin/dept/vo/post/PostRespVO.java +++ b/yudao-module-system/src/main/java/cn/iocoder/yudao/module/system/controller/admin/dept/vo/post/PostRespVO.java @@ -3,8 +3,8 @@ package cn.iocoder.yudao.module.system.controller.admin.dept.vo.post; import cn.iocoder.yudao.framework.excel.core.annotations.DictFormat; import cn.iocoder.yudao.framework.excel.core.convert.DictConvert; import cn.iocoder.yudao.module.system.enums.DictTypeConstants; -import cn.idev.excel.annotation.ExcelIgnoreUnannotated; -import cn.idev.excel.annotation.ExcelProperty; +import com.alibaba.excel.annotation.ExcelIgnoreUnannotated; +import com.alibaba.excel.annotation.ExcelProperty; import io.swagger.v3.oas.annotations.media.Schema; import lombok.Data; diff --git a/yudao-module-system/src/main/java/cn/iocoder/yudao/module/system/controller/admin/dept/vo/post/PostSimpleRespVO.java b/yudao-module-system/src/main/java/cn/iocoder/yudao/module/system/controller/admin/dept/vo/post/PostSimpleRespVO.java index d019ad7..72852a1 100644 --- a/yudao-module-system/src/main/java/cn/iocoder/yudao/module/system/controller/admin/dept/vo/post/PostSimpleRespVO.java +++ b/yudao-module-system/src/main/java/cn/iocoder/yudao/module/system/controller/admin/dept/vo/post/PostSimpleRespVO.java @@ -1,6 +1,6 @@ package cn.iocoder.yudao.module.system.controller.admin.dept.vo.post; -import cn.idev.excel.annotation.ExcelProperty; +import com.alibaba.excel.annotation.ExcelProperty; import io.swagger.v3.oas.annotations.media.Schema; import lombok.Data; diff --git a/yudao-module-system/src/main/java/cn/iocoder/yudao/module/system/controller/admin/dict/vo/data/DictDataRespVO.java b/yudao-module-system/src/main/java/cn/iocoder/yudao/module/system/controller/admin/dict/vo/data/DictDataRespVO.java index fce51e9..8857a70 100644 --- a/yudao-module-system/src/main/java/cn/iocoder/yudao/module/system/controller/admin/dict/vo/data/DictDataRespVO.java +++ b/yudao-module-system/src/main/java/cn/iocoder/yudao/module/system/controller/admin/dict/vo/data/DictDataRespVO.java @@ -3,8 +3,8 @@ package cn.iocoder.yudao.module.system.controller.admin.dict.vo.data; import cn.iocoder.yudao.framework.excel.core.annotations.DictFormat; import cn.iocoder.yudao.framework.excel.core.convert.DictConvert; import cn.iocoder.yudao.module.system.enums.DictTypeConstants; -import cn.idev.excel.annotation.ExcelIgnoreUnannotated; -import cn.idev.excel.annotation.ExcelProperty; +import com.alibaba.excel.annotation.ExcelIgnoreUnannotated; +import com.alibaba.excel.annotation.ExcelProperty; import io.swagger.v3.oas.annotations.media.Schema; import lombok.Data; diff --git a/yudao-module-system/src/main/java/cn/iocoder/yudao/module/system/controller/admin/dict/vo/type/DictTypeRespVO.java b/yudao-module-system/src/main/java/cn/iocoder/yudao/module/system/controller/admin/dict/vo/type/DictTypeRespVO.java index 2301a75..6ddd47b 100644 --- a/yudao-module-system/src/main/java/cn/iocoder/yudao/module/system/controller/admin/dict/vo/type/DictTypeRespVO.java +++ b/yudao-module-system/src/main/java/cn/iocoder/yudao/module/system/controller/admin/dict/vo/type/DictTypeRespVO.java @@ -3,8 +3,8 @@ package cn.iocoder.yudao.module.system.controller.admin.dict.vo.type; import cn.iocoder.yudao.framework.excel.core.annotations.DictFormat; import cn.iocoder.yudao.framework.excel.core.convert.DictConvert; import cn.iocoder.yudao.module.system.enums.DictTypeConstants; -import cn.idev.excel.annotation.ExcelIgnoreUnannotated; -import cn.idev.excel.annotation.ExcelProperty; +import com.alibaba.excel.annotation.ExcelIgnoreUnannotated; +import com.alibaba.excel.annotation.ExcelProperty; import io.swagger.v3.oas.annotations.media.Schema; import lombok.Data; diff --git a/yudao-module-system/src/main/java/cn/iocoder/yudao/module/system/controller/admin/logger/vo/loginlog/LoginLogRespVO.java b/yudao-module-system/src/main/java/cn/iocoder/yudao/module/system/controller/admin/logger/vo/loginlog/LoginLogRespVO.java index 9abe3aa..7b9cd16 100644 --- a/yudao-module-system/src/main/java/cn/iocoder/yudao/module/system/controller/admin/logger/vo/loginlog/LoginLogRespVO.java +++ b/yudao-module-system/src/main/java/cn/iocoder/yudao/module/system/controller/admin/logger/vo/loginlog/LoginLogRespVO.java @@ -3,8 +3,8 @@ package cn.iocoder.yudao.module.system.controller.admin.logger.vo.loginlog; import cn.iocoder.yudao.framework.excel.core.annotations.DictFormat; import cn.iocoder.yudao.framework.excel.core.convert.DictConvert; import cn.iocoder.yudao.module.system.enums.DictTypeConstants; -import cn.idev.excel.annotation.ExcelIgnoreUnannotated; -import cn.idev.excel.annotation.ExcelProperty; +import com.alibaba.excel.annotation.ExcelIgnoreUnannotated; +import com.alibaba.excel.annotation.ExcelProperty; import io.swagger.v3.oas.annotations.media.Schema; import lombok.Data; diff --git a/yudao-module-system/src/main/java/cn/iocoder/yudao/module/system/controller/admin/logger/vo/operatelog/OperateLogRespVO.java b/yudao-module-system/src/main/java/cn/iocoder/yudao/module/system/controller/admin/logger/vo/operatelog/OperateLogRespVO.java index a6d04d8..c3670ee 100644 --- a/yudao-module-system/src/main/java/cn/iocoder/yudao/module/system/controller/admin/logger/vo/operatelog/OperateLogRespVO.java +++ b/yudao-module-system/src/main/java/cn/iocoder/yudao/module/system/controller/admin/logger/vo/operatelog/OperateLogRespVO.java @@ -2,8 +2,8 @@ package cn.iocoder.yudao.module.system.controller.admin.logger.vo.operatelog; import cn.iocoder.yudao.framework.excel.core.annotations.DictFormat; import cn.iocoder.yudao.module.system.dal.dataobject.user.AdminUserDO; -import cn.idev.excel.annotation.ExcelIgnoreUnannotated; -import cn.idev.excel.annotation.ExcelProperty; +import com.alibaba.excel.annotation.ExcelIgnoreUnannotated; +import com.alibaba.excel.annotation.ExcelProperty; import cn.iocoder.yudao.module.system.enums.DictTypeConstants; import com.fhs.core.trans.anno.Trans; import com.fhs.core.trans.constant.TransType; diff --git a/yudao-module-system/src/main/java/cn/iocoder/yudao/module/system/controller/admin/permission/vo/role/RoleRespVO.java b/yudao-module-system/src/main/java/cn/iocoder/yudao/module/system/controller/admin/permission/vo/role/RoleRespVO.java index 02488b0..81bad5a 100644 --- a/yudao-module-system/src/main/java/cn/iocoder/yudao/module/system/controller/admin/permission/vo/role/RoleRespVO.java +++ b/yudao-module-system/src/main/java/cn/iocoder/yudao/module/system/controller/admin/permission/vo/role/RoleRespVO.java @@ -3,8 +3,8 @@ package cn.iocoder.yudao.module.system.controller.admin.permission.vo.role; import cn.iocoder.yudao.framework.excel.core.annotations.DictFormat; import cn.iocoder.yudao.framework.excel.core.convert.DictConvert; import cn.iocoder.yudao.module.system.enums.DictTypeConstants; -import cn.idev.excel.annotation.ExcelIgnoreUnannotated; -import cn.idev.excel.annotation.ExcelProperty; +import com.alibaba.excel.annotation.ExcelIgnoreUnannotated; +import com.alibaba.excel.annotation.ExcelProperty; import io.swagger.v3.oas.annotations.media.Schema; import lombok.Data; diff --git a/yudao-module-system/src/main/java/cn/iocoder/yudao/module/system/controller/admin/sms/vo/log/SmsLogRespVO.java b/yudao-module-system/src/main/java/cn/iocoder/yudao/module/system/controller/admin/sms/vo/log/SmsLogRespVO.java index 66a867f..77409ed 100644 --- a/yudao-module-system/src/main/java/cn/iocoder/yudao/module/system/controller/admin/sms/vo/log/SmsLogRespVO.java +++ b/yudao-module-system/src/main/java/cn/iocoder/yudao/module/system/controller/admin/sms/vo/log/SmsLogRespVO.java @@ -4,8 +4,8 @@ import cn.iocoder.yudao.framework.excel.core.annotations.DictFormat; import cn.iocoder.yudao.framework.excel.core.convert.DictConvert; import cn.iocoder.yudao.framework.excel.core.convert.JsonConvert; import cn.iocoder.yudao.module.system.enums.DictTypeConstants; -import cn.idev.excel.annotation.ExcelIgnoreUnannotated; -import cn.idev.excel.annotation.ExcelProperty; +import com.alibaba.excel.annotation.ExcelIgnoreUnannotated; +import com.alibaba.excel.annotation.ExcelProperty; import io.swagger.v3.oas.annotations.media.Schema; import lombok.Data; diff --git a/yudao-module-system/src/main/java/cn/iocoder/yudao/module/system/controller/admin/sms/vo/template/SmsTemplateRespVO.java b/yudao-module-system/src/main/java/cn/iocoder/yudao/module/system/controller/admin/sms/vo/template/SmsTemplateRespVO.java index 9caed5c..6b8aeca 100644 --- a/yudao-module-system/src/main/java/cn/iocoder/yudao/module/system/controller/admin/sms/vo/template/SmsTemplateRespVO.java +++ b/yudao-module-system/src/main/java/cn/iocoder/yudao/module/system/controller/admin/sms/vo/template/SmsTemplateRespVO.java @@ -3,8 +3,8 @@ package cn.iocoder.yudao.module.system.controller.admin.sms.vo.template; import cn.iocoder.yudao.framework.excel.core.annotations.DictFormat; import cn.iocoder.yudao.framework.excel.core.convert.DictConvert; import cn.iocoder.yudao.module.system.enums.DictTypeConstants; -import cn.idev.excel.annotation.ExcelIgnoreUnannotated; -import cn.idev.excel.annotation.ExcelProperty; +import com.alibaba.excel.annotation.ExcelIgnoreUnannotated; +import com.alibaba.excel.annotation.ExcelProperty; import io.swagger.v3.oas.annotations.media.Schema; import lombok.Data; diff --git a/yudao-module-system/src/main/java/cn/iocoder/yudao/module/system/controller/admin/tenant/vo/tenant/TenantRespVO.java b/yudao-module-system/src/main/java/cn/iocoder/yudao/module/system/controller/admin/tenant/vo/tenant/TenantRespVO.java index dd91812..d763c59 100644 --- a/yudao-module-system/src/main/java/cn/iocoder/yudao/module/system/controller/admin/tenant/vo/tenant/TenantRespVO.java +++ b/yudao-module-system/src/main/java/cn/iocoder/yudao/module/system/controller/admin/tenant/vo/tenant/TenantRespVO.java @@ -1,7 +1,7 @@ package cn.iocoder.yudao.module.system.controller.admin.tenant.vo.tenant; -import cn.idev.excel.annotation.ExcelIgnoreUnannotated; -import cn.idev.excel.annotation.ExcelProperty; +import com.alibaba.excel.annotation.ExcelIgnoreUnannotated; +import com.alibaba.excel.annotation.ExcelProperty; import cn.iocoder.yudao.framework.excel.core.annotations.DictFormat; import cn.iocoder.yudao.framework.excel.core.convert.DictConvert; import cn.iocoder.yudao.module.system.enums.DictTypeConstants; diff --git a/yudao-module-system/src/main/java/cn/iocoder/yudao/module/system/controller/admin/user/vo/user/UserImportExcelVO.java b/yudao-module-system/src/main/java/cn/iocoder/yudao/module/system/controller/admin/user/vo/user/UserImportExcelVO.java index f6400e1..a0a3360 100644 --- a/yudao-module-system/src/main/java/cn/iocoder/yudao/module/system/controller/admin/user/vo/user/UserImportExcelVO.java +++ b/yudao-module-system/src/main/java/cn/iocoder/yudao/module/system/controller/admin/user/vo/user/UserImportExcelVO.java @@ -1,9 +1,9 @@ package cn.iocoder.yudao.module.system.controller.admin.user.vo.user; -import cn.idev.excel.annotation.ExcelProperty; import cn.iocoder.yudao.framework.excel.core.annotations.DictFormat; import cn.iocoder.yudao.framework.excel.core.convert.DictConvert; import cn.iocoder.yudao.module.system.enums.DictTypeConstants; +import com.alibaba.excel.annotation.ExcelProperty; import lombok.AllArgsConstructor; import lombok.Builder; import lombok.Data; diff --git a/yudao-module-system/src/main/java/cn/iocoder/yudao/module/system/controller/admin/user/vo/user/UserRespVO.java b/yudao-module-system/src/main/java/cn/iocoder/yudao/module/system/controller/admin/user/vo/user/UserRespVO.java index ba32224..2837318 100644 --- a/yudao-module-system/src/main/java/cn/iocoder/yudao/module/system/controller/admin/user/vo/user/UserRespVO.java +++ b/yudao-module-system/src/main/java/cn/iocoder/yudao/module/system/controller/admin/user/vo/user/UserRespVO.java @@ -3,8 +3,8 @@ package cn.iocoder.yudao.module.system.controller.admin.user.vo.user; import cn.iocoder.yudao.framework.excel.core.annotations.DictFormat; import cn.iocoder.yudao.framework.excel.core.convert.DictConvert; import cn.iocoder.yudao.module.system.enums.DictTypeConstants; -import cn.idev.excel.annotation.ExcelIgnoreUnannotated; -import cn.idev.excel.annotation.ExcelProperty; +import com.alibaba.excel.annotation.ExcelIgnoreUnannotated; +import com.alibaba.excel.annotation.ExcelProperty; import io.swagger.v3.oas.annotations.media.Schema; import lombok.Data; diff --git a/yudao-server/pom.xml b/yudao-server/pom.xml index da92cdc..9e47723 100644 --- a/yudao-server/pom.xml +++ b/yudao-server/pom.xml @@ -95,26 +95,26 @@ - - cn.iocoder.boot - yudao-module-erp - ${revision} - - - - + - + - + + + + cn.iocoder.boot + yudao-module-iot-biz + ${revision} + + cn.iocoder.boot diff --git a/yudao-server/src/main/resources/application-dev.yaml b/yudao-server/src/main/resources/application-dev.yaml index c083572..5bb08a4 100644 --- a/yudao-server/src/main/resources/application-dev.yaml +++ b/yudao-server/src/main/resources/application-dev.yaml @@ -228,4 +228,17 @@ justauth: cache: type: REDIS prefix: 'social_auth_state:' # 缓存前缀,目前只对 Redis 缓存生效,默认 JUSTAUTH::STATE:: - timeout: 24h # 超时时长,目前只对 Redis 缓存生效,默认 3 分钟 \ No newline at end of file + timeout: 24h # 超时时长,目前只对 Redis 缓存生效,默认 3 分钟 + + + +emqx: + is-enable: true # 是否启用 MQTT + broker: tcp://47.106.185.127:1883 # EMQX 服务器地址(TCP 协议) + client-id: mqtt-client-besure-digital-center-dev # 客户端ID + user-name: admin # 用户名 + password: admin # 密码 + clean-session: true # 是否清空 session + reconnect: true # 是否自动断线重连 + timeout: 5 # 连接超时时间(秒) + keep-alive: 60 # 心跳间隔(秒) \ No newline at end of file diff --git a/yudao-server/src/main/resources/application.yaml b/yudao-server/src/main/resources/application.yaml index 57f1822..477cd31 100644 --- a/yudao-server/src/main/resources/application.yaml +++ b/yudao-server/src/main/resources/application.yaml @@ -311,6 +311,7 @@ yudao: enable: true ignore-urls: - /jmreport/* # 积木报表,无法携带租户编号 + - /admin-api/iot/mqtt-server/** ignore-visit-urls: - /admin-api/system/user/profile/** - /admin-api/system/auth/**