feat:迁移1.0采集设备模块

master
HuangHuiKang 2 months ago
parent e2f137fae2
commit 6a4c55a4c0

@ -23,8 +23,8 @@
<!-- <module>yudao-module-pay</module>-->
<!-- <module>yudao-module-mall</module>-->
<!-- <module>yudao-module-crm</module>-->
<module>yudao-module-erp</module>
<!-- <module>yudao-module-iot</module>-->
<!-- <module>yudao-module-erp</module>-->
<module>yudao-module-iot</module>
<!-- AI 大模型的开启,请参考 https://doc.iocoder.cn/ai/build/ 文档,对 JDK 版本要要求! -->
<!-- <module>yudao-module-ai</module>-->
</modules>

@ -25,7 +25,7 @@
<knife4j.version>4.5.0</knife4j.version>
<servlet.versoin>2.5</servlet.versoin>
<!-- DB 相关 -->
<druid.version>1.2.27</druid.version>
<druid.version>1.2.23</druid.version>
<mybatis.version>3.5.19</mybatis.version>
<mybatis-plus.version>3.5.15</mybatis-plus.version>
<mybatis-plus-join.version>1.5.5</mybatis-plus-join.version>
@ -53,6 +53,7 @@
<!-- 工具类相关 -->
<anji-plus-captcha.version>1.4.0</anji-plus-captcha.version>
<jsoup.version>1.21.2</jsoup.version>
<easyexcel.verion>4.0.3</easyexcel.verion>
<lombok.version>1.18.42</lombok.version>
<mapstruct.version>1.6.3</mapstruct.version>
<hutool-5.version>5.8.42</hutool-5.version>
@ -679,6 +680,14 @@
<version>${weixin-java.version}</version>
</dependency>
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>easyexcel</artifactId>
<version>${easyexcel.verion}</version>
</dependency>
<!-- 积木报表-->
<dependency>
<groupId>org.jeecgframework.jimureport</groupId>

@ -0,0 +1,15 @@
package cn.iocoder.yudao.framework.common.core;
/**
* Int
*
* @author
*/
public interface IntArrayValuable {
/**
* @return int
*/
int[] array();
}

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

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

@ -41,9 +41,13 @@
</dependency>
<!-- 工具类相关 -->
<!-- <dependency>-->
<!-- <groupId>cn.idev.excel</groupId>-->
<!-- <artifactId>fastexcel</artifactId>-->
<!-- </dependency>-->
<dependency>
<groupId>cn.idev.excel</groupId>
<artifactId>fastexcel</artifactId>
<groupId>com.alibaba</groupId>
<artifactId>easyexcel</artifactId>
</dependency>
<dependency>

@ -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;
/**

@ -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;
/**

@ -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
*

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

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

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

@ -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 <T> void write(HttpServletResponse response, String filename, String sheetName,
Class<T> head, List<T> 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 <T> List<T> read(MultipartFile file, Class<T> 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();
}
}

@ -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, "工作流标识已存在");
}

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

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

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

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

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

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

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

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

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

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

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

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

@ -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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

@ -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

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

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

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

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

@ -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

@ -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<Boolean> authDevice(@RequestBody IotDeviceAuthReqDTO authReqDTO) {
return success(deviceService.authDevice(authReqDTO));
}
@Override
@PostMapping(RpcConstants.RPC_API_PREFIX + "/iot/device/get") // 特殊:方便调用,暂时使用 POST实际更推荐 GET
@PermitAll
public CommonResult<IotDeviceRespDTO> 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<List<IotModbusDeviceConfigRespDTO>> getModbusDeviceConfigList(
@RequestBody IotModbusDeviceConfigListReqDTO listReqDTO) {
// 1. 获取 Modbus 连接配置
List<IotDeviceModbusConfigDO> configList = modbusConfigService.getDeviceModbusConfigList(listReqDTO);
if (CollUtil.isEmpty(configList)) {
return success(new ArrayList<>());
}
// 2. 组装返回结果
Set<Long> deviceIds = convertSet(configList, IotDeviceModbusConfigDO::getDeviceId);
Map<Long, IotDeviceDO> deviceMap = deviceService.getDeviceMap(deviceIds);
Map<Long, List<IotDeviceModbusPointDO>> pointMap = modbusPointService.getEnabledDeviceModbusPointMapByDeviceIds(deviceIds);
Map<Long, IotProductDO> productMap = productService.getProductMap(convertSet(deviceMap.values(), IotDeviceDO::getProductId));
List<IotModbusDeviceConfigRespDTO> 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<IotDeviceModbusPointDO> 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<IotDeviceRegisterRespDTO> registerDevice(@RequestBody IotDeviceRegisterReqDTO reqDTO) {
return success(deviceService.registerDevice(reqDTO));
}
@Override
@PostMapping(RpcConstants.RPC_API_PREFIX + "/iot/device/register-sub")
@PermitAll
public CommonResult<List<IotSubDeviceRegisterRespDTO>> registerSubDevices(@RequestBody IotSubDeviceRegisterFullReqDTO reqDTO) {
return success(deviceService.registerSubDevices(reqDTO));
}
}

@ -1,4 +0,0 @@
/**
* iot API API
*/
package cn.iocoder.yudao.module.iot.api;

@ -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<Long> createAlertConfig(@Valid @RequestBody IotAlertConfigSaveReqVO createReqVO) {
return success(alertConfigService.createAlertConfig(createReqVO));
}
@PutMapping("/update")
@Operation(summary = "更新告警配置")
@PreAuthorize("@ss.hasPermission('iot:alert-config:update')")
public CommonResult<Boolean> 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<Boolean> 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<IotAlertConfigRespVO> 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<PageResult<IotAlertConfigRespVO>> getAlertConfigPage(@Valid IotAlertConfigPageReqVO pageReqVO) {
PageResult<IotAlertConfigDO> pageResult = alertConfigService.getAlertConfigPage(pageReqVO);
// 转换返回
Map<Long, AdminUserRespDTO> 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<List<IotAlertConfigRespVO>> getAlertConfigSimpleList() {
List<IotAlertConfigDO> list = alertConfigService.getAlertConfigListByStatus(CommonStatusEnum.ENABLE.getStatus());
return success(convertList(list, config -> // 只返回 id、name 字段
new IotAlertConfigRespVO().setId(config.getId()).setName(config.getName())));
}
}

@ -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<IotAlertRecordRespVO> 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<PageResult<IotAlertRecordRespVO>> getAlertRecordPage(@Valid IotAlertRecordPageReqVO pageReqVO) {
PageResult<IotAlertRecordDO> pageResult = alertRecordService.getAlertRecordPage(pageReqVO);
return success(BeanUtils.toBean(pageResult, IotAlertRecordRespVO.class));
}
@PutMapping("/process")
@Operation(summary = "处理告警记录")
@PreAuthorize("@ss.hasPermission('iot:alert-record:process')")
public CommonResult<Boolean> processAlertRecord(@Valid @RequestBody IotAlertRecordProcessReqVO processReqVO) {
alertRecordService.processAlertRecordList(singleton(processReqVO.getId()), processReqVO.getProcessRemark());
return success(true);
}
}

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

@ -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<Long> sceneRuleIds;
@Schema(description = "接收的用户编号数组", requiredMode = Schema.RequiredMode.REQUIRED, example = "100,200")
private List<Long> receiveUserIds;
@Schema(description = "接收的用户名称数组", requiredMode = Schema.RequiredMode.REQUIRED, example = "张三,李四")
private List<String> receiveUserNames;
@Schema(description = "接收的类型数组", requiredMode = Schema.RequiredMode.REQUIRED, example = "1,2,3")
private List<Integer> receiveTypes;
@Schema(description = "创建时间", requiredMode = Schema.RequiredMode.REQUIRED)
private LocalDateTime createTime;
}

@ -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<Long> sceneRuleIds;
@Schema(description = "接收的用户编号数组")
@NotEmpty(message = "接收的用户编号数组不能为空")
private List<Long> receiveUserIds;
@Schema(description = "接收的类型数组")
@NotEmpty(message = "接收的类型数组不能为空")
private List<Integer> receiveTypes;
}

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

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

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

@ -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<DeviceDO> 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<Boolean> 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<Boolean> deleteDevice(@RequestParam("ids") String ids) {
// 将逗号分隔的字符串转换为Long类型的List
List<Long> 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<DeviceRespVO> 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<PageResult<DeviceRespVO>> getDevicePage(@Valid DevicePageReqVO pageReqVO) {
PageResult<DeviceRespVO> 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<DeviceRespVO> list = deviceService.getDevicePage(pageReqVO).getList();
// 导出 Excel
ExcelUtils.write(response, "物联设备.xls", "数据", DeviceRespVO.class,list);
}
@GetMapping("/deviceList")
// @PreAuthorize("@ss.hasPermission('iot:device:query')")
public CommonResult<List<DeviceRespVO>> deviceList(@Valid DevicePageReqVO pageReqVO) {
pageReqVO.setPageSize(PageParam.PAGE_SIZE_NONE);
List<DeviceRespVO> list = deviceService.getDevicePage(pageReqVO).getList();
return success(list);
}
@GetMapping("/noUsedlist")
@Operation(summary = "获得未关联设备台账列表")
@PreAuthorize("@ss.hasPermission('mes:device-ledger:query')")
public CommonResult<List<DeviceRespVO>> getDeviceLedgerListByNoUsed() {
DevicePageReqVO pageReqVO = new DevicePageReqVO();
pageReqVO.setPageSize(PageParam.PAGE_SIZE_NONE);
List<DeviceRespVO> list = deviceService.getDevicePage(pageReqVO).getList();
List<Long> ids = deviceService.deviceLedgerList();
List<DeviceRespVO> 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<List<DeviceRespVO>> getDeviceLedgerList2ByNoUsed(@RequestParam("id") Long id) {
DevicePageReqVO pageReqVO = new DevicePageReqVO();
pageReqVO.setPageSize(PageParam.PAGE_SIZE_NONE);
List<DeviceRespVO> list = deviceService.getDevicePage(pageReqVO).getList();
List<Long> ids = deviceService.deviceLedgerList();
List<DeviceRespVO> 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<Boolean> 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<Long> copyDevice(@RequestParam("id") Long id) {
return success(deviceService.copyDevice(id));
}
@GetMapping("/lineDevicePage")
@Operation(summary = "获得产线设备分页")
// @PreAuthorize("@ss.hasPermission('iot:device:lineDevicePage')")
public CommonResult<PageResult<LineDeviceRespVO>> lineDevicePage(@Valid LineDeviceRequestVO pageReqVO) {
PageResult<LineDeviceRespVO> 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<LineDeviceRespVO> 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<Map<String, List<Map<String, Object>>>> singleDevice(@RequestParam("deviceId") Long deviceId) throws JsonProcessingException {
Map<String, List<Map<String, Object>>> deviceContactModelDO=deviceService.singleDevice(deviceId);
return success(deviceContactModelDO);
}
@GetMapping("/historyRecord")
@Operation(summary = "历史记录查询")
// @PreAuthorize("@ss.hasPermission('iot:device:query')")
public CommonResult<PageResult<Map<String, Object>>> 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<String> attributeCodes,
@RequestParam(name = "pageNo", required = false, defaultValue = "1") Integer page,
@RequestParam(name = "pageSize", required = false, defaultValue = "10") Integer pageSize
) throws JsonProcessingException {
PageResult<Map<String, Object>> 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<DeviceOperationStatusRespVO> 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<PageResult<DeviceContactModelDO>> 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<List<DeviceContactModelDO>> 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<Long> createDeviceAttribute(@Valid @RequestBody DeviceAttributeDO deviceAttribute) {
return success(deviceService.createDeviceAttribute(deviceAttribute));
}
@PutMapping("/device-attribute/update")
@Operation(summary = "更新设备属性")
@PreAuthorize("@ss.hasPermission('iot:device:update')")
public CommonResult<Boolean> 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<Boolean> 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<DeviceAttributeDO> getDeviceAttribute(@RequestParam("id") Long id) {
return success(deviceService.getDeviceAttribute(id));
}
@GetMapping("/devicePointList")
@PreAuthorize("@ss.hasPermission('iot:device:query')")
public CommonResult<List<DevicePointRespVO>> devicePointList() {
return success( deviceService.devicePointList());
}
@PostMapping("/scheduledStart")
public CommonResult<Boolean> scheduledStart(@RequestParam("id") Long id) {
return success(deviceService.scheduledStart(id));
}
@PostMapping("/scheduledStop")
public CommonResult<Boolean> 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<List<Map<String, Object>>> 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<Boolean> updateDeviceEnabled(@Valid @RequestBody DeviceUpdateEnabledReqVO updateEnabledReqVO) throws MqttException {
deviceService.updateDeviceEnabled(updateEnabledReqVO);
return success(true);
}
}

@ -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<Long> createDevice(@Valid @RequestBody IotDeviceSaveReqVO createReqVO) {
return success(deviceService.createDevice(createReqVO));
}
@PutMapping("/update")
@Operation(summary = "更新设备")
@PreAuthorize("@ss.hasPermission('iot:device:update')")
public CommonResult<Boolean> updateDevice(@Valid @RequestBody IotDeviceSaveReqVO updateReqVO) {
deviceService.updateDevice(updateReqVO);
return success(true);
}
@PutMapping("/bind-gateway")
@Operation(summary = "绑定子设备到网关")
@PreAuthorize("@ss.hasPermission('iot:device:update')")
public CommonResult<Boolean> 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<Boolean> 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<List<IotDeviceRespVO>> getSubDeviceList(@RequestParam("gatewayId") Long gatewayId) {
List<IotDeviceDO> list = deviceService.getDeviceListByGatewayId(gatewayId);
if (CollUtil.isEmpty(list)) {
return success(Collections.emptyList());
}
// 补充产品名称
Map<Long, IotProductDO> 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<PageResult<IotDeviceRespVO>> getUnboundSubDevicePage(@Valid IotDevicePageReqVO pageReqVO) {
PageResult<IotDeviceDO> pageResult = deviceService.getUnboundSubDevicePage(pageReqVO);
if (CollUtil.isEmpty(pageResult.getList())) {
return success(PageResult.empty());
}
// 补充产品名称
Map<Long, IotProductDO> productMap = convertMap(productService.getProductList(), IotProductDO::getId);
PageResult<IotDeviceRespVO> 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<Boolean> 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<Boolean> 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<Boolean> deleteDeviceList(@RequestParam("ids") Collection<Long> 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<IotDeviceRespVO> 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<PageResult<IotDeviceRespVO>> getDevicePage(@Valid IotDevicePageReqVO pageReqVO) {
PageResult<IotDeviceDO> 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<PageResult<IotDeviceRespVO>> 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<Long> 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<List<IotDeviceRespVO>> getDeviceSimpleList(
@RequestParam(value = "deviceType", required = false) Integer deviceType,
@RequestParam(value = "productId", required = false) Long productId) {
List<IotDeviceDO> 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<List<IotDeviceRespVO>> getDeviceLocationList() {
// 1. 获取有位置信息的设备列表
List<IotDeviceDO> devices = deviceService.getDeviceListByHasLocation();
if (CollUtil.isEmpty(devices)) {
return success(Collections.emptyList());
}
// 2. 转换并返回
Map<Long, IotProductDO> 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<IotDeviceImportRespVO> importDevice(
@RequestParam("file") MultipartFile file,
@RequestParam(value = "updateSupport", required = false, defaultValue = "false") Boolean updateSupport)
throws Exception {
List<IotDeviceImportExcelVO> 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<IotDeviceImportExcelVO> 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<IotDeviceAuthInfoRespVO> getDeviceAuthInfo(@RequestParam("id") Long id) {
return success(deviceService.getDeviceAuthInfo(id));
}
// TODO @haohao可以使用 @RequestParam("productKey") String productKey, @RequestParam("deviceNames") List<String> deviceNames 来接收哇?
@GetMapping("/list-by-product-key-and-names")
@Operation(summary = "通过产品标识和设备名称列表获取设备")
@PreAuthorize("@ss.hasPermission('iot:device:query')")
public CommonResult<List<IotDeviceRespVO>> getDevicesByProductKeyAndNames(@Valid IotDeviceByProductKeyAndNamesReqVO reqVO) {
List<IotDeviceDO> devices = deviceService.getDeviceListByProductKeyAndNames(reqVO.getProductKey(), reqVO.getDeviceNames());
return success(BeanUtils.toBean(devices, IotDeviceRespVO.class));
}
}

@ -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<Long> createDeviceGroup(@Valid @RequestBody IotDeviceGroupSaveReqVO createReqVO) {
return success(deviceGroupService.createDeviceGroup(createReqVO));
}
@PutMapping("/update")
@Operation(summary = "更新设备分组")
@PreAuthorize("@ss.hasPermission('iot:device-group:update')")
public CommonResult<Boolean> 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<Boolean> 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<IotDeviceGroupRespVO> 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<PageResult<IotDeviceGroupRespVO>> getDeviceGroupPage(@Valid IotDeviceGroupPageReqVO pageReqVO) {
PageResult<IotDeviceGroupDO> 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<List<IotDeviceGroupRespVO>> getSimpleDeviceGroupList() {
List<IotDeviceGroupDO> list = deviceGroupService.getDeviceGroupListByStatus(CommonStatusEnum.ENABLE.getStatus());
return success(convertList(list, group -> // 只返回 id、name 字段
new IotDeviceGroupRespVO().setId(group.getId()).setName(group.getName())));
}
}

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

@ -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<PageResult<IotDeviceMessageRespVO>> getDeviceMessagePage(
@Valid IotDeviceMessagePageReqVO pageReqVO) {
PageResult<IotDeviceMessageDO> 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<PageResult<IotDeviceMessageRespPairVO>> getDeviceMessagePairPage(
@Valid IotDeviceMessagePageReqVO pageReqVO) {
// 1.1 先按照条件,查询 request 的消息(非 reply
pageReqVO.setReply(false);
PageResult<IotDeviceMessageDO> requestMessagePageResult = deviceMessageService.getDeviceMessagePage(pageReqVO);
if (CollUtil.isEmpty(requestMessagePageResult.getList())) {
return success(PageResult.empty());
}
// 1.2 接着按照 requestIds批量查询 reply 消息
List<String> requestIds = convertList(requestMessagePageResult.getList(), IotDeviceMessageDO::getRequestId);
List<IotDeviceMessageDO> replyMessageList = deviceMessageService.getDeviceMessageListByRequestIdsAndReply(
pageReqVO.getDeviceId(), requestIds, true);
Map<String, IotDeviceMessageDO> replyMessages = convertMap(replyMessageList, IotDeviceMessageDO::getRequestId);
// 2. 组装结果
List<IotDeviceMessageRespPairVO> 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<Boolean> sendDeviceMessage(@Valid @RequestBody IotDeviceMessageSendReqVO sendReqVO) {
deviceMessageService.sendDeviceMessage(BeanUtils.toBean(sendReqVO, IotDeviceMessage.class));
return success(true);
}
}

@ -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<Boolean> 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<IotDeviceModbusConfigRespVO> 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));
}
}

@ -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<Long> createDeviceModbusPoint(@Valid @RequestBody IotDeviceModbusPointSaveReqVO createReqVO) {
return success(modbusPointService.createDeviceModbusPoint(createReqVO));
}
@PutMapping("/update")
@Operation(summary = "更新设备 Modbus 点位配置")
@PreAuthorize("@ss.hasPermission('iot:device:update')")
public CommonResult<Boolean> 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<Boolean> 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<IotDeviceModbusPointRespVO> 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<PageResult<IotDeviceModbusPointRespVO>> getDeviceModbusPointPage(@Valid IotDeviceModbusPointPageReqVO pageReqVO) {
PageResult<IotDeviceModbusPointDO> pageResult = modbusPointService.getDeviceModbusPointPage(pageReqVO);
return success(BeanUtils.toBean(pageResult, IotDeviceModbusPointRespVO.class));
}
}

@ -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<List<IotDevicePropertyDetailRespVO>> getLatestDeviceProperties(
@RequestParam("deviceId") Long deviceId) {
// 1.1 获取设备信息
IotDeviceDO device = deviceService.getDevice(deviceId);
Assert.notNull(device, "设备不存在");
// 1.2 获取设备最新属性
Map<String, IotDevicePropertyDO> properties = devicePropertyService.getLatestDeviceProperties(deviceId);
// 1.3 根据 productId + type 查询属性类型的物模型
List<IotThingModelDO> 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<List<IotDevicePropertyRespVO>> getHistoryDevicePropertyList(
@Valid IotDevicePropertyHistoryListReqVO listReqVO) {
return success(devicePropertyService.getHistoryDevicePropertyList(listReqVO));
}
}

@ -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();
// }
}

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

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

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

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

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

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

@ -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<DeviceContactModelDO> 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<String> addresses = new ArrayList<>();
// List<Integer> 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<Object> 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<DeviceContactModelDO> 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<DeviceContactModelDO> getDevicePoints(Long deviceId) {
// LambdaQueryWrapper<DeviceContactModelDO> query = new LambdaQueryWrapper<>();
// query.eq(DeviceContactModelDO::getDeviceId, deviceId);
// return deviceContactModelMapper.selectList(query);
// }
//
//// /**
//// * 处理单个点位
//// */
//// private void processSinglePoint(DeviceContactModelDO point, List<DeviceContactModelDO> 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<DeviceContactModelDO> 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<DevicePointRulesDO> devicePointRulesDOList = devicePointRulesMapper.selectList(
// Wrappers.<DevicePointRulesDO>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<PointRulesRespVO> 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.<DeviceOperationRecordDO>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;
// }
// }
//
//
//}

@ -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<String, Task> taskBeans = new ConcurrentHashMap<>();
// 存储正在运行的任务
private final Map<Long, ScheduledFuture<?>> runningTasks = new ConcurrentHashMap<>();
private final Map<Long, TaskInfo> 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<Task> 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<String, Task> 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);
}
}

@ -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);
}
}
/**
* cron0
* @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) + "小时";
}
}
}

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

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

Some files were not shown because too many files have changed in this diff Show More

Loading…
Cancel
Save