diff --git a/plugins/enabled.txt b/plugins/enabled.txt index e69de29bb..a5ecb9379 100644 --- a/plugins/enabled.txt +++ b/plugins/enabled.txt @@ -0,0 +1,2 @@ +http-plugin +http-plugin@0.0.1 diff --git a/plugins/yudao-module-iot-http-plugin-2.2.0-snapshot.jar b/plugins/yudao-module-iot-http-plugin-2.2.0-snapshot.jar index 0300a632a..25120abe7 100644 Binary files a/plugins/yudao-module-iot-http-plugin-2.2.0-snapshot.jar and b/plugins/yudao-module-iot-http-plugin-2.2.0-snapshot.jar differ diff --git a/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/dal/dataobject/receivable/CrmReceivableDO.java b/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/dal/dataobject/receivable/CrmReceivableDO.java index 269cac6cc..c24614ad5 100644 --- a/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/dal/dataobject/receivable/CrmReceivableDO.java +++ b/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/dal/dataobject/receivable/CrmReceivableDO.java @@ -66,7 +66,9 @@ public class CrmReceivableDO extends BaseDO { */ private LocalDateTime returnTime; /** - * 回款方式,关联枚举{@link CrmReceivableReturnTypeEnum} + * 回款方式 + * + * 枚举 {@link CrmReceivableReturnTypeEnum} */ private Integer returnType; /** diff --git a/yudao-module-iot/yudao-module-iot-api/pom.xml b/yudao-module-iot/yudao-module-iot-api/pom.xml index a37094445..d2f83b785 100644 --- a/yudao-module-iot/yudao-module-iot-api/pom.xml +++ b/yudao-module-iot/yudao-module-iot-api/pom.xml @@ -22,6 +22,7 @@ yudao-common + org.pf4j pf4j-spring diff --git a/yudao-module-iot/yudao-module-iot-api/src/main/java/cn/iocoder/yudao/module/iot/api/ServiceRegistry.java b/yudao-module-iot/yudao-module-iot-api/src/main/java/cn/iocoder/yudao/module/iot/api/ServiceRegistry.java index 5c55bd89c..5603ad8d7 100644 --- a/yudao-module-iot/yudao-module-iot-api/src/main/java/cn/iocoder/yudao/module/iot/api/ServiceRegistry.java +++ b/yudao-module-iot/yudao-module-iot-api/src/main/java/cn/iocoder/yudao/module/iot/api/ServiceRegistry.java @@ -7,6 +7,7 @@ import java.util.Map; * 服务注册表 - 插架模块使用,无法使用 Spring 注入 */ public class ServiceRegistry { + private static final Map, Object> services = new HashMap<>(); /** @@ -31,4 +32,5 @@ public class ServiceRegistry { public static T getService(Class serviceClass) { return (T) services.get(serviceClass); } + } \ No newline at end of file diff --git a/yudao-module-iot/yudao-module-iot-api/src/main/java/cn/iocoder/yudao/module/iot/api/DeviceDataApi.java b/yudao-module-iot/yudao-module-iot-api/src/main/java/cn/iocoder/yudao/module/iot/api/device/DeviceDataApi.java similarity index 86% rename from yudao-module-iot/yudao-module-iot-api/src/main/java/cn/iocoder/yudao/module/iot/api/DeviceDataApi.java rename to yudao-module-iot/yudao-module-iot-api/src/main/java/cn/iocoder/yudao/module/iot/api/device/DeviceDataApi.java index 2f3ef6047..cb747f505 100644 --- a/yudao-module-iot/yudao-module-iot-api/src/main/java/cn/iocoder/yudao/module/iot/api/DeviceDataApi.java +++ b/yudao-module-iot/yudao-module-iot-api/src/main/java/cn/iocoder/yudao/module/iot/api/device/DeviceDataApi.java @@ -1,4 +1,4 @@ -package cn.iocoder.yudao.module.iot.api; +package cn.iocoder.yudao.module.iot.api.device; /** * 设备数据 API diff --git a/yudao-module-iot/yudao-module-iot-api/src/main/java/cn/iocoder/yudao/module/iot/enums/ErrorCodeConstants.java b/yudao-module-iot/yudao-module-iot-api/src/main/java/cn/iocoder/yudao/module/iot/enums/ErrorCodeConstants.java index a763924b5..4539f1259 100644 --- a/yudao-module-iot/yudao-module-iot-api/src/main/java/cn/iocoder/yudao/module/iot/enums/ErrorCodeConstants.java +++ b/yudao-module-iot/yudao-module-iot-api/src/main/java/cn/iocoder/yudao/module/iot/enums/ErrorCodeConstants.java @@ -14,7 +14,6 @@ public interface ErrorCodeConstants { ErrorCode PRODUCT_KEY_EXISTS = new ErrorCode(1_050_001_001, "产品标识已经存在"); ErrorCode PRODUCT_STATUS_NOT_DELETE = new ErrorCode(1_050_001_002, "产品状是发布状态,不允许删除"); ErrorCode PRODUCT_STATUS_NOT_ALLOW_THING_MODEL = new ErrorCode(1_050_001_003, "产品状是发布状态,不允许操作物模型"); - ErrorCode PRODUCT_DEVICE_NOT_EXISTS = new ErrorCode(1_050_001_004, "产品设备类型不存在"); // ========== 产品物模型 1-050-002-000 ============ ErrorCode THING_MODEL_NOT_EXISTS = new ErrorCode(1_050_002_000, "产品物模型不存在"); diff --git a/yudao-module-iot/yudao-module-iot-api/src/main/java/cn/iocoder/yudao/module/iot/enums/plugin/IotPluginDeployTypeEnum.java b/yudao-module-iot/yudao-module-iot-api/src/main/java/cn/iocoder/yudao/module/iot/enums/plugin/IotPluginDeployTypeEnum.java index 11898f683..9261e4ae1 100644 --- a/yudao-module-iot/yudao-module-iot-api/src/main/java/cn/iocoder/yudao/module/iot/enums/plugin/IotPluginDeployTypeEnum.java +++ b/yudao-module-iot/yudao-module-iot-api/src/main/java/cn/iocoder/yudao/module/iot/enums/plugin/IotPluginDeployTypeEnum.java @@ -34,12 +34,10 @@ public enum IotPluginDeployTypeEnum implements IntArrayValuable { } public static IotPluginDeployTypeEnum fromDeployType(Integer deployType) { - for (IotPluginDeployTypeEnum value : values()) { - if (value.getDeployType().equals(deployType)) { - return value; - } - } - return null; + return Arrays.stream(values()) + .filter(value -> value.getDeployType().equals(deployType)) + .findFirst() + .orElse(null); } public static boolean isValidDeployType(Integer deployType) { diff --git a/yudao-module-iot/yudao-module-iot-api/src/main/java/cn/iocoder/yudao/module/iot/enums/plugin/IotPluginStatusEnum.java b/yudao-module-iot/yudao-module-iot-api/src/main/java/cn/iocoder/yudao/module/iot/enums/plugin/IotPluginStatusEnum.java index 9aba854b0..aee2fbe0f 100644 --- a/yudao-module-iot/yudao-module-iot-api/src/main/java/cn/iocoder/yudao/module/iot/enums/plugin/IotPluginStatusEnum.java +++ b/yudao-module-iot/yudao-module-iot-api/src/main/java/cn/iocoder/yudao/module/iot/enums/plugin/IotPluginStatusEnum.java @@ -34,12 +34,10 @@ public enum IotPluginStatusEnum implements IntArrayValuable { } public static IotPluginStatusEnum fromState(Integer state) { - for (IotPluginStatusEnum value : values()) { - if (value.getStatus().equals(state)) { - return value; - } - } - return null; + return Arrays.stream(values()) + .filter(value -> value.getStatus().equals(state)) + .findFirst() + .orElse(null); } @Override diff --git a/yudao-module-iot/yudao-module-iot-api/src/main/java/cn/iocoder/yudao/module/iot/enums/plugin/IotPluginTypeEnum.java b/yudao-module-iot/yudao-module-iot-api/src/main/java/cn/iocoder/yudao/module/iot/enums/plugin/IotPluginTypeEnum.java index 27368d268..8b9cef2b4 100644 --- a/yudao-module-iot/yudao-module-iot-api/src/main/java/cn/iocoder/yudao/module/iot/enums/plugin/IotPluginTypeEnum.java +++ b/yudao-module-iot/yudao-module-iot-api/src/main/java/cn/iocoder/yudao/module/iot/enums/plugin/IotPluginTypeEnum.java @@ -35,14 +35,11 @@ public enum IotPluginTypeEnum implements IntArrayValuable { return ARRAYS; } - // TODO @haohao:可以使用 hutool 简化 public static IotPluginTypeEnum fromType(Integer type) { - for (IotPluginTypeEnum value : values()) { - if (value.getType().equals(type)) { - return value; - } - } - return null; + return Arrays.stream(values()) + .filter(value -> value.getType().equals(type)) + .findFirst() + .orElse(null); } public static boolean isValidType(Integer type) { diff --git a/yudao-module-iot/yudao-module-iot-api/src/main/java/cn/iocoder/yudao/module/iot/enums/thingmodel/IotDataSpecsDataTypeEnum.java b/yudao-module-iot/yudao-module-iot-api/src/main/java/cn/iocoder/yudao/module/iot/enums/thingmodel/IotDataSpecsDataTypeEnum.java new file mode 100644 index 000000000..5133b0038 --- /dev/null +++ b/yudao-module-iot/yudao-module-iot-api/src/main/java/cn/iocoder/yudao/module/iot/enums/thingmodel/IotDataSpecsDataTypeEnum.java @@ -0,0 +1,27 @@ +package cn.iocoder.yudao.module.iot.enums.thingmodel; + +import lombok.AllArgsConstructor; +import lombok.Getter; + +/** + * IoT 数据定义的数据类型枚举类 + * + * @author 芋道源码 + */ +@AllArgsConstructor +@Getter +public enum IotDataSpecsDataTypeEnum { + + INT("int"), + FLOAT("float"), + DOUBLE("double"), + ENUM("enum"), + BOOL("bool"), + TEXT("text"), + DATE("date"), + STRUCT("struct"), + ARRAY("array"); + + private final String dataType; + +} diff --git a/yudao-module-iot/yudao-module-iot-api/src/main/java/cn/iocoder/yudao/module/iot/enums/thingmodel/IotProductThingModelAccessModeEnum.java b/yudao-module-iot/yudao-module-iot-api/src/main/java/cn/iocoder/yudao/module/iot/enums/thingmodel/IotThingModelAccessModeEnum.java similarity index 73% rename from yudao-module-iot/yudao-module-iot-api/src/main/java/cn/iocoder/yudao/module/iot/enums/thingmodel/IotProductThingModelAccessModeEnum.java rename to yudao-module-iot/yudao-module-iot-api/src/main/java/cn/iocoder/yudao/module/iot/enums/thingmodel/IotThingModelAccessModeEnum.java index a75501803..0b18bbbf5 100644 --- a/yudao-module-iot/yudao-module-iot-api/src/main/java/cn/iocoder/yudao/module/iot/enums/thingmodel/IotProductThingModelAccessModeEnum.java +++ b/yudao-module-iot/yudao-module-iot-api/src/main/java/cn/iocoder/yudao/module/iot/enums/thingmodel/IotThingModelAccessModeEnum.java @@ -4,13 +4,13 @@ import lombok.AllArgsConstructor; import lombok.Getter; /** - * IOT 访问方式枚举类 + * IOT 产品物模型属性读取类型枚举 * * @author ahh */ @AllArgsConstructor @Getter -public enum IotProductThingModelAccessModeEnum { +public enum IotThingModelAccessModeEnum { READ_ONLY("r"), READ_WRITE("rw"); diff --git a/yudao-module-iot/yudao-module-iot-api/src/main/java/cn/iocoder/yudao/module/iot/enums/thingmodel/IotThingModelParamDirectionEnum.java b/yudao-module-iot/yudao-module-iot-api/src/main/java/cn/iocoder/yudao/module/iot/enums/thingmodel/IotThingModelParamDirectionEnum.java new file mode 100644 index 000000000..b1ae8a947 --- /dev/null +++ b/yudao-module-iot/yudao-module-iot-api/src/main/java/cn/iocoder/yudao/module/iot/enums/thingmodel/IotThingModelParamDirectionEnum.java @@ -0,0 +1,20 @@ +package cn.iocoder.yudao.module.iot.enums.thingmodel; + +import lombok.AllArgsConstructor; +import lombok.Getter; + +/** + * IOT 产品物模型参数是输入参数还是输出参数枚举 + * + * @author HUIHUI + */ +@AllArgsConstructor +@Getter +public enum IotThingModelParamDirectionEnum { + + INPUT("input"), // 输入参数 + OUTPUT("output"); // 输出参数 + + private final String direction; + +} diff --git a/yudao-module-iot/yudao-module-iot-api/src/main/java/cn/iocoder/yudao/module/iot/enums/thingmodel/IotThingModelServiceCallTypeEnum.java b/yudao-module-iot/yudao-module-iot-api/src/main/java/cn/iocoder/yudao/module/iot/enums/thingmodel/IotThingModelServiceCallTypeEnum.java new file mode 100644 index 000000000..66ca6bec5 --- /dev/null +++ b/yudao-module-iot/yudao-module-iot-api/src/main/java/cn/iocoder/yudao/module/iot/enums/thingmodel/IotThingModelServiceCallTypeEnum.java @@ -0,0 +1,20 @@ +package cn.iocoder.yudao.module.iot.enums.thingmodel; + +import lombok.AllArgsConstructor; +import lombok.Getter; + +/** + * IOT 产品物模型服务调用方式枚举 + * + * @author HUIHUI + */ +@AllArgsConstructor +@Getter +public enum IotThingModelServiceCallTypeEnum { + + ASYNC("async"), // 异步调用 + SYNC("sync"); // 同步调用 + + private final String type; + +} diff --git a/yudao-module-iot/yudao-module-iot-api/src/main/java/cn/iocoder/yudao/module/iot/enums/thingmodel/IotThingModelServiceEventTypeEnum.java b/yudao-module-iot/yudao-module-iot-api/src/main/java/cn/iocoder/yudao/module/iot/enums/thingmodel/IotThingModelServiceEventTypeEnum.java new file mode 100644 index 000000000..b87d7eb98 --- /dev/null +++ b/yudao-module-iot/yudao-module-iot-api/src/main/java/cn/iocoder/yudao/module/iot/enums/thingmodel/IotThingModelServiceEventTypeEnum.java @@ -0,0 +1,21 @@ +package cn.iocoder.yudao.module.iot.enums.thingmodel; + +import lombok.AllArgsConstructor; +import lombok.Getter; + +/** + * IOT 产品物模型事件类型枚举 + * + * @author HUIHUI + */ +@AllArgsConstructor +@Getter +public enum IotThingModelServiceEventTypeEnum { + + INFO("info"), // 信息 + ALERT("alert"), // 告警 + ERROR("error"); // 故障 + + private final String type; + +} diff --git a/yudao-module-iot/yudao-module-iot-api/src/main/java/cn/iocoder/yudao/module/iot/enums/thingmodel/IotProductThingModelTypeEnum.java b/yudao-module-iot/yudao-module-iot-api/src/main/java/cn/iocoder/yudao/module/iot/enums/thingmodel/IotThingModelTypeEnum.java similarity index 74% rename from yudao-module-iot/yudao-module-iot-api/src/main/java/cn/iocoder/yudao/module/iot/enums/thingmodel/IotProductThingModelTypeEnum.java rename to yudao-module-iot/yudao-module-iot-api/src/main/java/cn/iocoder/yudao/module/iot/enums/thingmodel/IotThingModelTypeEnum.java index 153e93ecc..93fdc0ca4 100644 --- a/yudao-module-iot/yudao-module-iot-api/src/main/java/cn/iocoder/yudao/module/iot/enums/thingmodel/IotProductThingModelTypeEnum.java +++ b/yudao-module-iot/yudao-module-iot-api/src/main/java/cn/iocoder/yudao/module/iot/enums/thingmodel/IotThingModelTypeEnum.java @@ -13,13 +13,13 @@ import java.util.Arrays; */ @AllArgsConstructor @Getter -public enum IotProductThingModelTypeEnum implements IntArrayValuable { +public enum IotThingModelTypeEnum implements IntArrayValuable { PROPERTY(1, "属性"), SERVICE(2, "服务"), EVENT(3, "事件"); - public static final int[] ARRAYS = Arrays.stream(values()).mapToInt(IotProductThingModelTypeEnum::getType).toArray(); + public static final int[] ARRAYS = Arrays.stream(values()).mapToInt(IotThingModelTypeEnum::getType).toArray(); /** * 类型 @@ -30,8 +30,8 @@ public enum IotProductThingModelTypeEnum implements IntArrayValuable { */ private final String description; - public static IotProductThingModelTypeEnum valueOfType(Integer type) { - for (IotProductThingModelTypeEnum value : values()) { + public static IotThingModelTypeEnum valueOfType(Integer type) { + for (IotThingModelTypeEnum value : values()) { if (value.getType().equals(type)) { return value; } diff --git a/yudao-module-iot/yudao-module-iot-biz/pom.xml b/yudao-module-iot/yudao-module-iot-biz/pom.xml index c0810a183..d0ed0bcac 100644 --- a/yudao-module-iot/yudao-module-iot-biz/pom.xml +++ b/yudao-module-iot/yudao-module-iot-biz/pom.xml @@ -25,13 +25,6 @@ ${revision} - - cn.iocoder.boot - yudao-module-iot-plugin-api - 0.0.1 - compile - - cn.iocoder.boot yudao-spring-boot-starter-biz-tenant @@ -81,6 +74,7 @@ org.pf4j pf4j-spring + org.slf4j diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/api/device/DeviceDataApiImpl.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/api/device/DeviceDataApiImpl.java index 59ae850a1..b4a2a62db 100644 --- a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/api/device/DeviceDataApiImpl.java +++ b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/api/device/DeviceDataApiImpl.java @@ -1,7 +1,6 @@ package cn.iocoder.yudao.module.iot.api.device; -import cn.iocoder.yudao.module.iot.api.DeviceDataApi; -import cn.iocoder.yudao.module.iot.service.device.IotDeviceDataService; +import cn.iocoder.yudao.module.iot.service.device.IotDevicePropertyDataService; import org.springframework.stereotype.Service; import org.springframework.validation.annotation.Validated; @@ -15,11 +14,11 @@ import javax.annotation.Resource; public class DeviceDataApiImpl implements DeviceDataApi { @Resource - private IotDeviceDataService iotDeviceDataService; + private IotDevicePropertyDataService deviceDataService; @Override public void saveDeviceData(String productKey, String deviceName, String message) { - iotDeviceDataService.saveDeviceData(productKey, deviceName, message); + deviceDataService.saveDeviceData(productKey, deviceName, message); } } \ No newline at end of file diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/api/package-info.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/api/package-info.java index c0f3d748a..07852180d 100644 --- a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/api/package-info.java +++ b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/api/package-info.java @@ -1 +1,6 @@ +/** + * 占位 + * + * TODO 芋艿:后续删除 + */ package cn.iocoder.yudao.module.iot.api; \ No newline at end of file diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/device/IotDeviceDataController.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/device/IotDeviceDataController.java index 77bc78c66..a626951ed 100644 --- a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/device/IotDeviceDataController.java +++ b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/device/IotDeviceDataController.java @@ -7,7 +7,7 @@ import cn.iocoder.yudao.module.iot.controller.admin.device.vo.deviceData.IotDevi import cn.iocoder.yudao.module.iot.controller.admin.device.vo.deviceData.IotDeviceDataRespVO; import cn.iocoder.yudao.module.iot.dal.dataobject.device.IotDeviceDataDO; import cn.iocoder.yudao.module.iot.controller.admin.device.vo.deviceData.IotTimeDataRespVO; -import cn.iocoder.yudao.module.iot.service.device.IotDeviceDataService; +import cn.iocoder.yudao.module.iot.service.device.IotDevicePropertyDataService; import io.swagger.v3.oas.annotations.Operation; import io.swagger.v3.oas.annotations.tags.Tag; import jakarta.annotation.Resource; @@ -29,7 +29,7 @@ import static cn.iocoder.yudao.framework.common.pojo.CommonResult.success; public class IotDeviceDataController { @Resource - private IotDeviceDataService deviceDataService; + private IotDevicePropertyDataService deviceDataService; // TODO @浩浩:这里的 /latest-list,包括方法名。 @GetMapping("/latest") diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/plugininfo/PluginInfoController.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/plugin/PluginInfoController.java similarity index 74% rename from yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/plugininfo/PluginInfoController.java rename to yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/plugin/PluginInfoController.java index b7efd088e..96eb3dda6 100644 --- a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/plugininfo/PluginInfoController.java +++ b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/plugin/PluginInfoController.java @@ -1,13 +1,14 @@ -package cn.iocoder.yudao.module.iot.controller.admin.plugininfo; +package cn.iocoder.yudao.module.iot.controller.admin.plugin; 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.plugininfo.vo.PluginInfoPageReqVO; -import cn.iocoder.yudao.module.iot.controller.admin.plugininfo.vo.PluginInfoRespVO; -import cn.iocoder.yudao.module.iot.controller.admin.plugininfo.vo.PluginInfoSaveReqVO; +import cn.iocoder.yudao.module.iot.controller.admin.plugin.vo.PluginInfoImportReqVO; +import cn.iocoder.yudao.module.iot.controller.admin.plugin.vo.PluginInfoPageReqVO; +import cn.iocoder.yudao.module.iot.controller.admin.plugin.vo.PluginInfoRespVO; +import cn.iocoder.yudao.module.iot.controller.admin.plugin.vo.PluginInfoSaveReqVO; import cn.iocoder.yudao.module.iot.dal.dataobject.plugininfo.PluginInfoDO; -import cn.iocoder.yudao.module.iot.service.plugininfo.PluginInfoService; +import cn.iocoder.yudao.module.iot.service.plugin.PluginInfoService; import io.swagger.v3.oas.annotations.Operation; import io.swagger.v3.oas.annotations.Parameter; import io.swagger.v3.oas.annotations.tags.Tag; @@ -16,11 +17,8 @@ import jakarta.validation.Valid; 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 static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception; import static cn.iocoder.yudao.framework.common.pojo.CommonResult.success; -import static cn.iocoder.yudao.module.infra.enums.ErrorCodeConstants.FILE_IS_EMPTY; @Tag(name = "管理后台 - IoT 插件信息") @RestController @@ -72,16 +70,11 @@ public class PluginInfoController { return success(BeanUtils.toBean(pageResult, PluginInfoRespVO.class)); } - @RequestMapping(value = "/update-jar", - method = {RequestMethod.POST, RequestMethod.PUT}) // 解决 uni-app 不支持 Put 上传文件的问题 - @Operation(summary = "上传Jar包") - public CommonResult uploadJar( - @RequestParam("id") Long id, - @RequestParam("jar") MultipartFile file) throws Exception { - if (file.isEmpty()) { - throw exception(FILE_IS_EMPTY); - } - pluginInfoService.uploadJar(id, file); + @PostMapping("/upload-file") + @Operation(summary = "上传插件文件") + @PreAuthorize("@ss.hasPermission('iot:plugin-info:update')") + public CommonResult uploadFile(@Valid PluginInfoImportReqVO reqVO) { + pluginInfoService.uploadFile(reqVO.getId(), reqVO.getFile()); return success(true); } diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/plugin/vo/PluginInfoImportReqVO.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/plugin/vo/PluginInfoImportReqVO.java new file mode 100644 index 000000000..e71e4c484 --- /dev/null +++ b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/plugin/vo/PluginInfoImportReqVO.java @@ -0,0 +1,19 @@ +package cn.iocoder.yudao.module.iot.controller.admin.plugin.vo; + +import io.swagger.v3.oas.annotations.media.Schema; +import jakarta.validation.constraints.NotNull; +import lombok.Data; +import org.springframework.web.multipart.MultipartFile; + +@Schema(description = "管理后台 - IoT 插件上传 Request VO") +@Data +public class PluginInfoImportReqVO { + + @Schema(description = "主键ID", requiredMode = Schema.RequiredMode.REQUIRED, example = "11546") + private Long id; + + @Schema(description = "插件文件", requiredMode = Schema.RequiredMode.REQUIRED) + @NotNull(message = "插件文件不能为空") + private MultipartFile file; + +} \ No newline at end of file diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/plugin/vo/PluginInfoPageReqVO.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/plugin/vo/PluginInfoPageReqVO.java new file mode 100644 index 000000000..7d4677dd4 --- /dev/null +++ b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/plugin/vo/PluginInfoPageReqVO.java @@ -0,0 +1,17 @@ +package cn.iocoder.yudao.module.iot.controller.admin.plugin.vo; + +import cn.iocoder.yudao.framework.common.pojo.PageParam; +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.Data; + +@Schema(description = "管理后台 - IoT 插件信息分页 Request VO") +@Data +public class PluginInfoPageReqVO extends PageParam { + + @Schema(description = "插件名称", example = "http") + private String name; + + @Schema(description = "状态") + private Integer status; + +} \ No newline at end of file diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/plugininfo/vo/PluginInfoRespVO.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/plugin/vo/PluginInfoRespVO.java similarity index 88% rename from yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/plugininfo/vo/PluginInfoRespVO.java rename to yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/plugin/vo/PluginInfoRespVO.java index 6f081764a..514ba4f1f 100644 --- a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/plugininfo/vo/PluginInfoRespVO.java +++ b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/plugin/vo/PluginInfoRespVO.java @@ -1,4 +1,4 @@ -package cn.iocoder.yudao.module.iot.controller.admin.plugininfo.vo; +package cn.iocoder.yudao.module.iot.controller.admin.plugin.vo; import com.alibaba.excel.annotation.ExcelIgnoreUnannotated; import com.alibaba.excel.annotation.ExcelProperty; @@ -16,9 +16,9 @@ public class PluginInfoRespVO { @ExcelProperty("主键 ID") private Long id; - @Schema(description = "插件包 ID", requiredMode = Schema.RequiredMode.REQUIRED, example = "24627") - @ExcelProperty("插件包 ID") - private String pluginId; + @Schema(description = "插件包标识符", requiredMode = Schema.RequiredMode.REQUIRED, example = "24627") + @ExcelProperty("插件包标识符") + private String pluginKey; @Schema(description = "插件名称", requiredMode = Schema.RequiredMode.REQUIRED, example = "赵六") @ExcelProperty("插件名称") @@ -34,7 +34,7 @@ public class PluginInfoRespVO { @Schema(description = "插件包文件名", requiredMode = Schema.RequiredMode.REQUIRED) @ExcelProperty("插件包文件名") - private String file; + private String fileName; @Schema(description = "插件版本", requiredMode = Schema.RequiredMode.REQUIRED) @ExcelProperty("插件版本") diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/plugininfo/vo/PluginInfoSaveReqVO.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/plugin/vo/PluginInfoSaveReqVO.java similarity index 86% rename from yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/plugininfo/vo/PluginInfoSaveReqVO.java rename to yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/plugin/vo/PluginInfoSaveReqVO.java index 8ce254a3a..9a9848130 100644 --- a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/plugininfo/vo/PluginInfoSaveReqVO.java +++ b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/plugin/vo/PluginInfoSaveReqVO.java @@ -1,4 +1,4 @@ -package cn.iocoder.yudao.module.iot.controller.admin.plugininfo.vo; +package cn.iocoder.yudao.module.iot.controller.admin.plugin.vo; import io.swagger.v3.oas.annotations.media.Schema; import lombok.*; @@ -10,8 +10,8 @@ public class PluginInfoSaveReqVO { @Schema(description = "主键ID", requiredMode = Schema.RequiredMode.REQUIRED, example = "11546") private Long id; - @Schema(description = "插件包id", requiredMode = Schema.RequiredMode.REQUIRED, example = "24627") - private String pluginId; + @Schema(description = "插件包标识符", requiredMode = Schema.RequiredMode.REQUIRED, example = "24627") + private String pluginKey; @Schema(description = "插件名称", requiredMode = Schema.RequiredMode.REQUIRED, example = "赵六") private String name; @@ -23,7 +23,7 @@ public class PluginInfoSaveReqVO { private Integer deployType; @Schema(description = "插件包文件名", requiredMode = Schema.RequiredMode.REQUIRED) - private String file; + private String fileName; @Schema(description = "插件版本", requiredMode = Schema.RequiredMode.REQUIRED) private String version; diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/plugininstance/vo/PluginInstancePageReqVO.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/plugin/vo/PluginInstancePageReqVO.java similarity index 94% rename from yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/plugininstance/vo/PluginInstancePageReqVO.java rename to yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/plugin/vo/PluginInstancePageReqVO.java index 9fccdc0ae..23f885d5a 100644 --- a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/plugininstance/vo/PluginInstancePageReqVO.java +++ b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/plugin/vo/PluginInstancePageReqVO.java @@ -1,4 +1,4 @@ -package cn.iocoder.yudao.module.iot.controller.admin.plugininstance.vo; +package cn.iocoder.yudao.module.iot.controller.admin.plugin.vo; import lombok.*; import io.swagger.v3.oas.annotations.media.Schema; diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/plugininstance/vo/PluginInstanceRespVO.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/plugin/vo/PluginInstanceRespVO.java similarity index 95% rename from yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/plugininstance/vo/PluginInstanceRespVO.java rename to yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/plugin/vo/PluginInstanceRespVO.java index 92edca821..e92b49f1e 100644 --- a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/plugininstance/vo/PluginInstanceRespVO.java +++ b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/plugin/vo/PluginInstanceRespVO.java @@ -1,4 +1,4 @@ -package cn.iocoder.yudao.module.iot.controller.admin.plugininstance.vo; +package cn.iocoder.yudao.module.iot.controller.admin.plugin.vo; import io.swagger.v3.oas.annotations.media.Schema; import lombok.*; diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/plugininstance/vo/PluginInstanceSaveReqVO.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/plugin/vo/PluginInstanceSaveReqVO.java similarity index 94% rename from yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/plugininstance/vo/PluginInstanceSaveReqVO.java rename to yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/plugin/vo/PluginInstanceSaveReqVO.java index 95db299d1..802a7f01f 100644 --- a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/plugininstance/vo/PluginInstanceSaveReqVO.java +++ b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/plugin/vo/PluginInstanceSaveReqVO.java @@ -1,4 +1,4 @@ -package cn.iocoder.yudao.module.iot.controller.admin.plugininstance.vo; +package cn.iocoder.yudao.module.iot.controller.admin.plugin.vo; import io.swagger.v3.oas.annotations.media.Schema; import lombok.*; diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/plugininfo/PluginController.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/plugininfo/PluginController.java deleted file mode 100644 index 321eef9b6..000000000 --- a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/plugininfo/PluginController.java +++ /dev/null @@ -1,116 +0,0 @@ -package cn.iocoder.yudao.module.iot.controller.admin.plugininfo; - -import jakarta.annotation.Resource; -import org.pf4j.spring.SpringPluginManager; -import org.springframework.beans.factory.annotation.Value; -import org.springframework.http.HttpStatus; -import org.springframework.http.ResponseEntity; -import org.springframework.web.bind.annotation.*; -import org.springframework.web.multipart.MultipartFile; - -import javax.annotation.security.PermitAll; -import java.io.IOException; -import java.nio.file.Files; -import java.nio.file.Path; -import java.nio.file.Paths; -import java.nio.file.StandardCopyOption; -import java.util.List; -import java.util.stream.Collectors; - -/** - * 插件 Controller 测试用例 - */ -@RestController -@RequestMapping("/iot/plugins") -public class PluginController { - - @Resource - private SpringPluginManager springPluginManager; - - @Value("${pf4j.pluginsDir}") - private String pluginsDir; - - /** - * 上传插件 JAR 文件并加载插件 - * - * @param file 上传的 JAR 文件 - * @return 上传结果 - */ - @PermitAll - @PostMapping("/upload") - public ResponseEntity uploadPlugin(@RequestParam("file") MultipartFile file) { - if (file.isEmpty()) { - return ResponseEntity.status(HttpStatus.BAD_REQUEST).body("上传的文件为空"); - } - - // 确保插件目录存在 - Path pluginsPath = Paths.get(pluginsDir); - try { - if (!Files.exists(pluginsPath)) { - Files.createDirectories(pluginsPath); - } - - // 保存上传的 JAR 文件到插件目录 - String filename = file.getOriginalFilename(); - if (filename == null || (!filename.endsWith(".jar") && !filename.endsWith(".zip"))) { - return ResponseEntity.status(HttpStatus.BAD_REQUEST).body("上传的文件不是 JAR 或 ZIP 文件"); - } - - Path jarPath = pluginsPath.resolve(filename); - - Files.copy(file.getInputStream(), jarPath, StandardCopyOption.REPLACE_EXISTING); - - // 加载插件 - String pluginId = springPluginManager.loadPlugin(jarPath.toAbsolutePath()); - - // 启动插件 - springPluginManager.startPlugin(pluginId); - - return ResponseEntity.ok("插件上传并加载成功"); - } catch (IOException e) { - return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR).body("上传插件时发生错误: " + e.getMessage()); - } catch (Exception e) { - return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR).body("加载插件时发生错误: " + e.getMessage()); - } - } - - /** - * 卸载指定插件 - * - * @param pluginId 插件 ID - * @return 卸载结果 - */ - @PermitAll - @DeleteMapping("/unload/{pluginId}") - public ResponseEntity unloadPlugin(@PathVariable String pluginId) { - if (springPluginManager.getPlugins().stream().noneMatch(plugin -> plugin.getDescriptor().getPluginId().equals(pluginId))) { - return ResponseEntity.status(HttpStatus.BAD_REQUEST).body("插件未加载: " + pluginId); - } - - springPluginManager.stopPlugin(pluginId); - springPluginManager.unloadPlugin(pluginId); - - // 删除插件 JAR 文件(可选) -// PluginWrapper plugin = pluginManager.getPlugin(pluginId); -// PluginDescriptor descriptor = plugin.getDescriptor(); -// Path jarPath = Paths.get(pluginsDir).resolve(descriptor.getPluginId() + ".jar"); -// Files.deleteIfExists(jarPath); - - return ResponseEntity.ok("插件卸载成功: " + pluginId); - } - - /** - * 列出所有已加载的插件 - * - * @return 插件列表 - */ - @PermitAll - @GetMapping("/list") - public ResponseEntity> listPlugins() { - List plugins = springPluginManager.getPlugins().stream() - .map(plugin -> plugin.getDescriptor().getPluginId()) - .collect(Collectors.toList()); - return ResponseEntity.ok(plugins); - } - -} \ No newline at end of file diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/plugininfo/vo/PluginInfoPageReqVO.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/plugininfo/vo/PluginInfoPageReqVO.java deleted file mode 100644 index a3b36da9d..000000000 --- a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/plugininfo/vo/PluginInfoPageReqVO.java +++ /dev/null @@ -1,59 +0,0 @@ -package cn.iocoder.yudao.module.iot.controller.admin.plugininfo.vo; - -import cn.iocoder.yudao.framework.common.validation.InEnum; -import cn.iocoder.yudao.module.iot.enums.plugin.IotPluginTypeEnum; -import lombok.*; -import io.swagger.v3.oas.annotations.media.Schema; -import cn.iocoder.yudao.framework.common.pojo.PageParam; -import org.springframework.format.annotation.DateTimeFormat; -import java.time.LocalDateTime; - -import static cn.iocoder.yudao.framework.common.util.date.DateUtils.FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND; - -// TODO @haohao:只查询必要字段哈 -@Schema(description = "管理后台 - IoT 插件信息分页 Request VO") -@Data -public class PluginInfoPageReqVO extends PageParam { - - @Schema(description = "插件包 ID ", example = "24627") - private String pluginId; - - @Schema(description = "插件名称", example = "赵六") - private String name; - - @Schema(description = "描述", example = "你猜") - private String description; - - @Schema(description = "部署方式", example = "2") - private Integer deployType; - - @Schema(description = "插件包文件名") - private String file; - - @Schema(description = "插件版本") - private String version; - - @Schema(description = "插件类型", example = "2") - @InEnum(IotPluginTypeEnum.class) - private Integer type; - - @Schema(description = "设备插件协议类型") - private String protocol; - - @Schema(description = "状态") - private Integer status; - - @Schema(description = "插件配置项描述信息") - private String configSchema; - - @Schema(description = "插件配置信息") - private String config; - - @Schema(description = "插件脚本") - private String script; - - @Schema(description = "创建时间") - @DateTimeFormat(pattern = FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND) - private LocalDateTime[] createTime; - -} \ No newline at end of file diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/plugininstance/PluginInstanceController.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/plugininstance/PluginInstanceController.java deleted file mode 100644 index 9382f8c6d..000000000 --- a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/plugininstance/PluginInstanceController.java +++ /dev/null @@ -1,94 +0,0 @@ -package cn.iocoder.yudao.module.iot.controller.admin.plugininstance; - -import org.springframework.web.bind.annotation.*; -import jakarta.annotation.Resource; -import org.springframework.validation.annotation.Validated; -import org.springframework.security.access.prepost.PreAuthorize; -import io.swagger.v3.oas.annotations.tags.Tag; -import io.swagger.v3.oas.annotations.Parameter; -import io.swagger.v3.oas.annotations.Operation; - -import jakarta.validation.*; -import jakarta.servlet.http.*; -import java.util.*; -import java.io.IOException; - -import cn.iocoder.yudao.framework.common.pojo.PageParam; -import cn.iocoder.yudao.framework.common.pojo.PageResult; -import cn.iocoder.yudao.framework.common.pojo.CommonResult; -import cn.iocoder.yudao.framework.common.util.object.BeanUtils; -import static cn.iocoder.yudao.framework.common.pojo.CommonResult.success; - -import cn.iocoder.yudao.framework.excel.core.util.ExcelUtils; - -import cn.iocoder.yudao.framework.apilog.core.annotation.ApiAccessLog; -import static cn.iocoder.yudao.framework.apilog.core.enums.OperateTypeEnum.*; - -import cn.iocoder.yudao.module.iot.controller.admin.plugininstance.vo.*; -import cn.iocoder.yudao.module.iot.dal.dataobject.plugininstance.PluginInstanceDO; -import cn.iocoder.yudao.module.iot.service.plugininstance.PluginInstanceService; - -@Tag(name = "管理后台 - IoT 插件实例") -@RestController -@RequestMapping("/iot/plugin-instance") -@Validated -public class PluginInstanceController { - - @Resource - private PluginInstanceService pluginInstanceService; - - @PostMapping("/create") - @Operation(summary = "创建IoT 插件实例") - @PreAuthorize("@ss.hasPermission('iot:plugin-instance:create')") - public CommonResult createPluginInstance(@Valid @RequestBody PluginInstanceSaveReqVO createReqVO) { - return success(pluginInstanceService.createPluginInstance(createReqVO)); - } - - @PutMapping("/update") - @Operation(summary = "更新IoT 插件实例") - @PreAuthorize("@ss.hasPermission('iot:plugin-instance:update')") - public CommonResult updatePluginInstance(@Valid @RequestBody PluginInstanceSaveReqVO updateReqVO) { - pluginInstanceService.updatePluginInstance(updateReqVO); - return success(true); - } - - @DeleteMapping("/delete") - @Operation(summary = "删除IoT 插件实例") - @Parameter(name = "id", description = "编号", required = true) - @PreAuthorize("@ss.hasPermission('iot:plugin-instance:delete')") - public CommonResult deletePluginInstance(@RequestParam("id") Long id) { - pluginInstanceService.deletePluginInstance(id); - return success(true); - } - - @GetMapping("/get") - @Operation(summary = "获得IoT 插件实例") - @Parameter(name = "id", description = "编号", required = true, example = "1024") - @PreAuthorize("@ss.hasPermission('iot:plugin-instance:query')") - public CommonResult getPluginInstance(@RequestParam("id") Long id) { - PluginInstanceDO pluginInstance = pluginInstanceService.getPluginInstance(id); - return success(BeanUtils.toBean(pluginInstance, PluginInstanceRespVO.class)); - } - - @GetMapping("/page") - @Operation(summary = "获得IoT 插件实例分页") - @PreAuthorize("@ss.hasPermission('iot:plugin-instance:query')") - public CommonResult> getPluginInstancePage(@Valid PluginInstancePageReqVO pageReqVO) { - PageResult pageResult = pluginInstanceService.getPluginInstancePage(pageReqVO); - return success(BeanUtils.toBean(pageResult, PluginInstanceRespVO.class)); - } - - @GetMapping("/export-excel") - @Operation(summary = "导出IoT 插件实例 Excel") - @PreAuthorize("@ss.hasPermission('iot:plugin-instance:export')") - @ApiAccessLog(operateType = EXPORT) - public void exportPluginInstanceExcel(@Valid PluginInstancePageReqVO pageReqVO, - HttpServletResponse response) throws IOException { - pageReqVO.setPageSize(PageParam.PAGE_SIZE_NONE); - List list = pluginInstanceService.getPluginInstancePage(pageReqVO).getList(); - // 导出 Excel - ExcelUtils.write(response, "IoT 插件实例.xls", "数据", PluginInstanceRespVO.class, - BeanUtils.toBean(list, PluginInstanceRespVO.class)); - } - -} \ No newline at end of file diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/thingmodel/IotProductThingModelController.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/thingmodel/IotProductThingModelController.java deleted file mode 100644 index 9b94def42..000000000 --- a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/thingmodel/IotProductThingModelController.java +++ /dev/null @@ -1,84 +0,0 @@ -package cn.iocoder.yudao.module.iot.controller.admin.thingmodel; - -import cn.iocoder.yudao.framework.common.pojo.CommonResult; -import cn.iocoder.yudao.framework.common.pojo.PageResult; -import cn.iocoder.yudao.framework.common.util.object.BeanUtils; -import cn.iocoder.yudao.module.iot.controller.admin.thingmodel.vo.IotProductThingModelPageReqVO; -import cn.iocoder.yudao.module.iot.controller.admin.thingmodel.vo.IotProductThingModelRespVO; -import cn.iocoder.yudao.module.iot.controller.admin.thingmodel.vo.IotProductThingModelSaveReqVO; -import cn.iocoder.yudao.module.iot.convert.thingmodel.IotProductThingModelConvert; -import cn.iocoder.yudao.module.iot.dal.dataobject.thingmodel.IotProductThingModelDO; -import cn.iocoder.yudao.module.iot.service.thingmodel.IotProductThingModelService; -import io.swagger.v3.oas.annotations.Operation; -import io.swagger.v3.oas.annotations.Parameter; -import io.swagger.v3.oas.annotations.tags.Tag; -import jakarta.annotation.Resource; -import jakarta.validation.Valid; -import org.springframework.security.access.prepost.PreAuthorize; -import org.springframework.validation.annotation.Validated; -import org.springframework.web.bind.annotation.*; - -import java.util.List; - -import static cn.iocoder.yudao.framework.common.pojo.CommonResult.success; - -@Tag(name = "管理后台 - IoT 产品物模型") -@RestController -@RequestMapping("/iot/product-thing-model") -@Validated -public class IotProductThingModelController { - - @Resource - private IotProductThingModelService thingModelService; - - @PostMapping("/create") - @Operation(summary = "创建产品物模型") - @PreAuthorize("@ss.hasPermission('iot:product-thing-model:create')") - public CommonResult createProductThingModel(@Valid @RequestBody IotProductThingModelSaveReqVO createReqVO) { - return success(thingModelService.createProductThingModel(createReqVO)); - } - - @PutMapping("/update") - @Operation(summary = "更新产品物模型") - @PreAuthorize("@ss.hasPermission('iot:product-thing-model:update')") - public CommonResult updateProductThingModel(@Valid @RequestBody IotProductThingModelSaveReqVO updateReqVO) { - thingModelService.updateProductThingModel(updateReqVO); - return success(true); - } - - @DeleteMapping("/delete") - @Operation(summary = "删除产品物模型") - @Parameter(name = "id", description = "编号", required = true) - @PreAuthorize("@ss.hasPermission('iot:product-thing-model:delete')") - public CommonResult deleteProductThingModel(@RequestParam("id") Long id) { - thingModelService.deleteProductThingModel(id); - return success(true); - } - - @GetMapping("/get") - @Operation(summary = "获得产品物模型") - @Parameter(name = "id", description = "编号", required = true) - @PreAuthorize("@ss.hasPermission('iot:product-thing-model:query')") - public CommonResult getProductThingModel(@RequestParam("id") Long id) { - IotProductThingModelDO thingModel = thingModelService.getProductThingModel(id); - return success(IotProductThingModelConvert.INSTANCE.convert(thingModel)); - } - - @GetMapping("/list-by-product-id") - @Operation(summary = "获得产品物模型") - @Parameter(name = "productId", description = "产品ID", required = true, example = "1024") - @PreAuthorize("@ss.hasPermission('iot:product-thing-model:query')") - public CommonResult> getProductThingModelListByProductId(@RequestParam("productId") Long productId) { - List list = thingModelService.getProductThingModelListByProductId(productId); - return success(IotProductThingModelConvert.INSTANCE.convertList(list)); - } - - @GetMapping("/page") - @Operation(summary = "获得产品物模型分页") - @PreAuthorize("@ss.hasPermission('iot:product-thing-model:query')") - public CommonResult> getProductThingModelPage(@Valid IotProductThingModelPageReqVO pageReqVO) { - PageResult pageResult = thingModelService.getProductThingModelPage(pageReqVO); - return success(BeanUtils.toBean(pageResult, IotProductThingModelRespVO.class)); - } - -} diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/thingmodel/IotProductThingModelController.http b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/thingmodel/IotThingModelController.http similarity index 93% rename from yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/thingmodel/IotProductThingModelController.http rename to yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/thingmodel/IotThingModelController.http index 7742705ef..1e1f72103 100644 --- a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/thingmodel/IotProductThingModelController.http +++ b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/thingmodel/IotThingModelController.http @@ -25,8 +25,7 @@ Authorization: Bearer {{token}} "defaultValue": "30", "unit": "%", "unitName": "百分比" - }, - "description": "当前温度值" + } } } @@ -46,7 +45,6 @@ Authorization: Bearer {{token}} "property": { "identifier": "switch", "name": "开关", - "description": "温度计开关", "accessMode": "rw", "required": true, "dataType": "bool", @@ -81,7 +79,6 @@ Authorization: Bearer {{token}} "property": { "identifier": "argb", "name": "温度计 argb 颜色", - "description": "温度计 argb 颜色", "accessMode": "rw", "required": true, "dataType": "array", @@ -93,7 +90,6 @@ Authorization: Bearer {{token}} { "identifier": "switch", "name": "开关", - "description": "温度计开关", "accessMode": "rw", "required": true, "dataType": "struct", @@ -126,8 +122,7 @@ Authorization: Bearer {{token}} "defaultValue": "30", "unit": "%", "unitName": "百分比" - }, - "description": "当前温度值" + } } ] } @@ -151,7 +146,6 @@ Authorization: Bearer {{token}} "property": { "identifier": "switch", "name": "开关", - "description": "温度计开关", "accessMode": "r", "required": true, "dataType": "bool", @@ -176,7 +170,7 @@ tenant-id: {{adminTenentId}} Authorization: Bearer {{token}} ### 请求 /iot/product-thing-model/get 接口 => 成功 -GET {{baseUrl}}/iot/product-thing-model/get?id=40 +GET {{baseUrl}}/iot/product-thing-model/get?id=67 tenant-id: {{adminTenentId}} Authorization: Bearer {{token}} diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/thingmodel/IotThingModelController.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/thingmodel/IotThingModelController.java new file mode 100644 index 000000000..4397a742f --- /dev/null +++ b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/thingmodel/IotThingModelController.java @@ -0,0 +1,84 @@ +package cn.iocoder.yudao.module.iot.controller.admin.thingmodel; + +import cn.iocoder.yudao.framework.common.pojo.CommonResult; +import cn.iocoder.yudao.framework.common.pojo.PageResult; +import cn.iocoder.yudao.framework.common.util.object.BeanUtils; +import cn.iocoder.yudao.module.iot.controller.admin.thingmodel.vo.IotThingModelPageReqVO; +import cn.iocoder.yudao.module.iot.controller.admin.thingmodel.vo.IotThingModelRespVO; +import cn.iocoder.yudao.module.iot.controller.admin.thingmodel.vo.IotThingModelSaveReqVO; +import cn.iocoder.yudao.module.iot.convert.thingmodel.IotThingModelConvert; +import cn.iocoder.yudao.module.iot.dal.dataobject.thingmodel.IotThingModelDO; +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 jakarta.annotation.Resource; +import jakarta.validation.Valid; +import org.springframework.security.access.prepost.PreAuthorize; +import org.springframework.validation.annotation.Validated; +import org.springframework.web.bind.annotation.*; + +import java.util.List; + +import static cn.iocoder.yudao.framework.common.pojo.CommonResult.success; + +@Tag(name = "管理后台 - IoT 产品物模型") +@RestController +@RequestMapping("/iot/thing-model") +@Validated +public class IotThingModelController { + + @Resource + private IotThingModelService thingModelService; + + @PostMapping("/create") + @Operation(summary = "创建产品物模型") + @PreAuthorize("@ss.hasPermission('iot:thing-model:create')") + public CommonResult createThingModel(@Valid @RequestBody IotThingModelSaveReqVO createReqVO) { + return success(thingModelService.createThingModel(createReqVO)); + } + + @PutMapping("/update") + @Operation(summary = "更新产品物模型") + @PreAuthorize("@ss.hasPermission('iot:thing-model:update')") + public CommonResult updateThingModel(@Valid @RequestBody IotThingModelSaveReqVO updateReqVO) { + thingModelService.updateThingModel(updateReqVO); + return success(true); + } + + @DeleteMapping("/delete") + @Operation(summary = "删除产品物模型") + @Parameter(name = "id", description = "编号", required = true) + @PreAuthorize("@ss.hasPermission('iot:thing-model:delete')") + public CommonResult deleteThingModel(@RequestParam("id") Long id) { + thingModelService.deleteThingModel(id); + return success(true); + } + + @GetMapping("/get") + @Operation(summary = "获得产品物模型") + @Parameter(name = "id", description = "编号", required = true) + @PreAuthorize("@ss.hasPermission('iot:thing-model:query')") + public CommonResult getThingModel(@RequestParam("id") Long id) { + IotThingModelDO thingModel = thingModelService.getThingModel(id); + return success(IotThingModelConvert.INSTANCE.convert(thingModel)); + } + + @GetMapping("/list-by-product-id") + @Operation(summary = "获得产品物模型") + @Parameter(name = "productId", description = "产品ID", required = true, example = "1024") + @PreAuthorize("@ss.hasPermission('iot:thing-model:query')") + public CommonResult> getThingModelListByProductId(@RequestParam("productId") Long productId) { + List list = thingModelService.getThingModelListByProductId(productId); + return success(IotThingModelConvert.INSTANCE.convertList(list)); + } + + @GetMapping("/page") + @Operation(summary = "获得产品物模型分页") + @PreAuthorize("@ss.hasPermission('iot:thing-model:query')") + public CommonResult> getThingModelPage(@Valid IotThingModelPageReqVO pageReqVO) { + PageResult pageResult = thingModelService.getProductThingModelPage(pageReqVO); + return success(BeanUtils.toBean(pageResult, IotThingModelRespVO.class)); + } + +} diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/thingmodel/model/ThingModelEvent.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/thingmodel/model/ThingModelEvent.java index 718c7b60e..0ed5d29d1 100644 --- a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/thingmodel/model/ThingModelEvent.java +++ b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/thingmodel/model/ThingModelEvent.java @@ -1,9 +1,15 @@ package cn.iocoder.yudao.module.iot.controller.admin.thingmodel.model; -import cn.iocoder.yudao.module.iot.controller.admin.thingmodel.model.dataType.ThingModelArgument; +import cn.iocoder.yudao.module.iot.enums.thingmodel.IotThingModelServiceEventTypeEnum; import lombok.Data; + import java.util.List; +/** + * 物模型中的事件 + * + * @author HUIHUI + */ @Data public class ThingModelEvent { @@ -16,17 +22,24 @@ public class ThingModelEvent { */ private String name; /** - * 事件描述 + * 是否是标准品类的必选事件 */ - private String description; - + private Boolean required; /** * 事件类型 * - * "info"、"alert"、"error" + * 枚举 {@link IotThingModelServiceEventTypeEnum} */ private String type; - private List outputData; + /** + * 事件的输出参数 + * + * 输出参数定义事件调用后返回的结果或反馈信息,用于确认操作结果或提供额外的信息。 + */ + private List outputParams; + /** + * 标识设备需要执行的具体操作 + */ private String method; } diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/thingmodel/model/ThingModelParam.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/thingmodel/model/ThingModelParam.java new file mode 100644 index 000000000..9cddf1d29 --- /dev/null +++ b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/thingmodel/model/ThingModelParam.java @@ -0,0 +1,50 @@ +package cn.iocoder.yudao.module.iot.controller.admin.thingmodel.model; + +import cn.iocoder.yudao.module.iot.controller.admin.thingmodel.model.dataType.ThingModelDataSpecs; +import cn.iocoder.yudao.module.iot.enums.thingmodel.IotThingModelParamDirectionEnum; +import lombok.Data; + +import java.util.List; + +/** + * IOT 产品物模型中的参数 + * + * @author HUIHUI + */ +@Data +public class ThingModelParam { + + /** + * 参数标识符 + */ + private String identifier; + /** + * 参数名称 + */ + private String name; + /** + * 用于区分输入或输出参数 + * + * 枚举 {@link IotThingModelParamDirectionEnum} + */ + private String direction; + /** + * 参数的序号。从 0 开始排序,且不能重复。 + * + * TODO 考虑要不要序号,感觉是要的, 先留一手看看 + */ + private Integer paraOrder; + /** + * 参数值的数据类型,与 dataSpecs 的 dataType 保持一致 + */ + private String dataType; + /** + * 参数值的数据类型(dataType)为非列表型(int、float、double、text、date、array)的数据规范存储在 dataSpecs 中 + */ + private ThingModelDataSpecs dataSpecs; + /** + * 参数值的数据类型(dataType)为列表型(enum、bool、struct)的数据规范存储在 dataSpecsList 中 + */ + private List dataSpecsList; + +} diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/thingmodel/model/ThingModelProperty.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/thingmodel/model/ThingModelProperty.java index 0c9105a8b..db7333df2 100644 --- a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/thingmodel/model/ThingModelProperty.java +++ b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/thingmodel/model/ThingModelProperty.java @@ -1,7 +1,7 @@ package cn.iocoder.yudao.module.iot.controller.admin.thingmodel.model; import cn.iocoder.yudao.module.iot.controller.admin.thingmodel.model.dataType.ThingModelDataSpecs; -import cn.iocoder.yudao.module.iot.enums.thingmodel.IotProductThingModelAccessModeEnum; +import cn.iocoder.yudao.module.iot.enums.thingmodel.IotThingModelAccessModeEnum; import lombok.Data; import java.util.List; @@ -24,13 +24,10 @@ public class ThingModelProperty { * 属性名称 */ private String name; - /** - * 属性描述 - */ - private String description; /** * 云端可以对该属性进行的操作类型 - * 关联枚举 {@link IotProductThingModelAccessModeEnum} + * + * 枚举 {@link IotThingModelAccessModeEnum} */ private String accessMode; /** @@ -42,6 +39,8 @@ public class ThingModelProperty { private Boolean required; /** * 数据类型,与 dataSpecs 的 dataType 保持一致 + * + * 枚举 {@link cn.iocoder.yudao.module.iot.enums.thingmodel.IotDataSpecsDataTypeEnum} */ private String dataType; /** diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/thingmodel/model/ThingModelService.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/thingmodel/model/ThingModelService.java index ec4bd34e9..6803c7103 100644 --- a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/thingmodel/model/ThingModelService.java +++ b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/thingmodel/model/ThingModelService.java @@ -1,9 +1,15 @@ package cn.iocoder.yudao.module.iot.controller.admin.thingmodel.model; -import cn.iocoder.yudao.module.iot.controller.admin.thingmodel.model.dataType.ThingModelArgument; +import cn.iocoder.yudao.module.iot.enums.thingmodel.IotThingModelServiceCallTypeEnum; import lombok.Data; + import java.util.List; +/** + * 物模型中的服务 + * + * @author HUIHUI + */ @Data public class ThingModelService { @@ -16,18 +22,30 @@ public class ThingModelService { */ private String name; /** - * 服务描述 + * 是否是标准品类的必选服务 */ - private String description; - + private Boolean required; /** * 调用类型 * - * "sync"、"async" + * 枚举 {@link IotThingModelServiceCallTypeEnum} */ private String callType; - private List inputData; - private List outputData; + /** + * 服务的输入参数 + * + * 输入参数定义服务调用时所需提供的信息,用于控制设备行为或执行特定任务 + */ + private List inputParams; + /** + * 服务的输出参数 + * + * 输出参数定义服务调用后返回的结果或反馈信息,用于确认操作结果或提供额外的信息。 + */ + private List outputParams; + /** + * 标识设备需要执行的具体操作 + */ private String method; } diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/thingmodel/model/dataType/ThingModelArgument.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/thingmodel/model/dataType/ThingModelArgument.java deleted file mode 100644 index 96f51150f..000000000 --- a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/thingmodel/model/dataType/ThingModelArgument.java +++ /dev/null @@ -1,26 +0,0 @@ -package cn.iocoder.yudao.module.iot.controller.admin.thingmodel.model.dataType; - -import cn.iocoder.yudao.module.iot.controller.admin.thingmodel.model.ThingModelProperty; -import com.fasterxml.jackson.annotation.JsonIgnoreProperties; -import lombok.Data; - -@Data -@JsonIgnoreProperties(ignoreUnknown = true) -public class ThingModelArgument { - - public static final String DIRECTION_INPUT = "input"; - public static final String DIRECTION_OUTPUT = "output"; - - private String identifier; - private String name; - /** - * 物模型中的属性 - */ - private ThingModelProperty property; - /** - * 用于区分输入或输出参数,"input" 或 "output" - */ - private String direction; - private String description; - -} diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/thingmodel/model/dataType/ThingModelDateOrTextDataSpecs.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/thingmodel/model/dataType/ThingModelDateOrTextDataSpecs.java index 8d5ddff62..9d2c88ae1 100644 --- a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/thingmodel/model/dataType/ThingModelDateOrTextDataSpecs.java +++ b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/thingmodel/model/dataType/ThingModelDateOrTextDataSpecs.java @@ -20,7 +20,7 @@ public class ThingModelDateOrTextDataSpecs extends ThingModelDataSpecs { * 数据长度,单位为字节。取值不能超过 2048。 * 当 dataType 为 text 时,需传入该参数。 */ - private Long length; + private Integer length; /** * 默认值,可选参数,用于存储默认值。 */ diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/thingmodel/model/dataType/ThingModelStructDataSpecs.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/thingmodel/model/dataType/ThingModelStructDataSpecs.java index e0f5bb539..8fcaefdc5 100644 --- a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/thingmodel/model/dataType/ThingModelStructDataSpecs.java +++ b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/thingmodel/model/dataType/ThingModelStructDataSpecs.java @@ -1,6 +1,6 @@ package cn.iocoder.yudao.module.iot.controller.admin.thingmodel.model.dataType; -import cn.iocoder.yudao.module.iot.enums.thingmodel.IotProductThingModelAccessModeEnum; +import cn.iocoder.yudao.module.iot.enums.thingmodel.IotThingModelAccessModeEnum; import com.fasterxml.jackson.annotation.JsonIgnoreProperties; import lombok.Data; import lombok.EqualsAndHashCode; @@ -25,20 +25,14 @@ public class ThingModelStructDataSpecs extends ThingModelDataSpecs { * 属性名称 */ private String name; - /** - * 属性描述 - */ - private String description; /** * 云端可以对该属性进行的操作类型 - * 关联枚举 {@link IotProductThingModelAccessModeEnum} + * + * 枚举 {@link IotThingModelAccessModeEnum} */ private String accessMode; /** - * 是否是标准品类的必选服务。 - * - * - true:是 - * - false:否 + * 是否是标准品类的必选服务 */ private Boolean required; /** diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/thingmodel/vo/IotProductThingModelPageReqVO.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/thingmodel/vo/IotThingModelPageReqVO.java similarity index 82% rename from yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/thingmodel/vo/IotProductThingModelPageReqVO.java rename to yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/thingmodel/vo/IotThingModelPageReqVO.java index a15ae98a1..314441fb6 100644 --- a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/thingmodel/vo/IotProductThingModelPageReqVO.java +++ b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/thingmodel/vo/IotThingModelPageReqVO.java @@ -2,7 +2,7 @@ package cn.iocoder.yudao.module.iot.controller.admin.thingmodel.vo; import cn.iocoder.yudao.framework.common.pojo.PageParam; import cn.iocoder.yudao.framework.common.validation.InEnum; -import cn.iocoder.yudao.module.iot.enums.thingmodel.IotProductThingModelTypeEnum; +import cn.iocoder.yudao.module.iot.enums.thingmodel.IotThingModelTypeEnum; import io.swagger.v3.oas.annotations.media.Schema; import jakarta.validation.constraints.NotNull; import lombok.Data; @@ -13,7 +13,7 @@ import lombok.ToString; @Data @EqualsAndHashCode(callSuper = true) @ToString(callSuper = true) -public class IotProductThingModelPageReqVO extends PageParam { +public class IotThingModelPageReqVO extends PageParam { @Schema(description = "功能标识") private String identifier; @@ -22,7 +22,7 @@ public class IotProductThingModelPageReqVO extends PageParam { private String name; @Schema(description = "功能类型", example = "1") - @InEnum(IotProductThingModelTypeEnum.class) + @InEnum(IotThingModelTypeEnum.class) private Integer type; @Schema(description = "产品ID", requiredMode = Schema.RequiredMode.REQUIRED) diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/thingmodel/vo/IotProductThingModelRespVO.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/thingmodel/vo/IotThingModelRespVO.java similarity index 98% rename from yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/thingmodel/vo/IotProductThingModelRespVO.java rename to yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/thingmodel/vo/IotThingModelRespVO.java index 8d9c370d9..88c197bab 100644 --- a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/thingmodel/vo/IotProductThingModelRespVO.java +++ b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/thingmodel/vo/IotThingModelRespVO.java @@ -13,7 +13,7 @@ import java.time.LocalDateTime; @Schema(description = "管理后台 - IoT 产品物模型 Response VO") @Data @ExcelIgnoreUnannotated -public class IotProductThingModelRespVO { +public class IotThingModelRespVO { @Schema(description = "产品ID", requiredMode = Schema.RequiredMode.REQUIRED, example = "21816") @ExcelProperty("产品ID") diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/thingmodel/vo/IotProductThingModelSaveReqVO.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/thingmodel/vo/IotThingModelSaveReqVO.java similarity index 92% rename from yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/thingmodel/vo/IotProductThingModelSaveReqVO.java rename to yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/thingmodel/vo/IotThingModelSaveReqVO.java index 5f2be4847..0eb37231b 100644 --- a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/thingmodel/vo/IotProductThingModelSaveReqVO.java +++ b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/thingmodel/vo/IotThingModelSaveReqVO.java @@ -4,7 +4,7 @@ import cn.iocoder.yudao.framework.common.validation.InEnum; import cn.iocoder.yudao.module.iot.controller.admin.thingmodel.model.ThingModelEvent; import cn.iocoder.yudao.module.iot.controller.admin.thingmodel.model.ThingModelProperty; import cn.iocoder.yudao.module.iot.controller.admin.thingmodel.model.ThingModelService; -import cn.iocoder.yudao.module.iot.enums.thingmodel.IotProductThingModelTypeEnum; +import cn.iocoder.yudao.module.iot.enums.thingmodel.IotThingModelTypeEnum; import io.swagger.v3.oas.annotations.media.Schema; import jakarta.validation.constraints.NotEmpty; import jakarta.validation.constraints.NotNull; @@ -12,7 +12,7 @@ import lombok.Data; @Schema(description = "管理后台 - IoT 产品物模型新增/修改 Request VO") @Data -public class IotProductThingModelSaveReqVO { +public class IotThingModelSaveReqVO { @Schema(description = "编号", example = "1") private Long id; @@ -38,7 +38,7 @@ public class IotProductThingModelSaveReqVO { @Schema(description = "功能类型", requiredMode = Schema.RequiredMode.REQUIRED) @NotNull(message = "功能类型不能为空") - @InEnum(IotProductThingModelTypeEnum.class) + @InEnum(IotThingModelTypeEnum.class) private Integer type; @Schema(description = "属性", requiredMode = Schema.RequiredMode.REQUIRED) diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/convert/thingmodel/IotProductThingModelConvert.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/convert/thingmodel/IotThingModelConvert.java similarity index 58% rename from yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/convert/thingmodel/IotProductThingModelConvert.java rename to yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/convert/thingmodel/IotThingModelConvert.java index cb10c62b5..2e9b5441c 100644 --- a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/convert/thingmodel/IotProductThingModelConvert.java +++ b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/convert/thingmodel/IotThingModelConvert.java @@ -3,10 +3,10 @@ package cn.iocoder.yudao.module.iot.convert.thingmodel; import cn.iocoder.yudao.module.iot.controller.admin.thingmodel.model.ThingModelEvent; import cn.iocoder.yudao.module.iot.controller.admin.thingmodel.model.ThingModelProperty; import cn.iocoder.yudao.module.iot.controller.admin.thingmodel.model.ThingModelService; -import cn.iocoder.yudao.module.iot.controller.admin.thingmodel.vo.IotProductThingModelRespVO; -import cn.iocoder.yudao.module.iot.controller.admin.thingmodel.vo.IotProductThingModelSaveReqVO; -import cn.iocoder.yudao.module.iot.dal.dataobject.thingmodel.IotProductThingModelDO; -import cn.iocoder.yudao.module.iot.enums.thingmodel.IotProductThingModelTypeEnum; +import cn.iocoder.yudao.module.iot.controller.admin.thingmodel.vo.IotThingModelRespVO; +import cn.iocoder.yudao.module.iot.controller.admin.thingmodel.vo.IotThingModelSaveReqVO; +import cn.iocoder.yudao.module.iot.dal.dataobject.thingmodel.IotThingModelDO; +import cn.iocoder.yudao.module.iot.enums.thingmodel.IotThingModelTypeEnum; import org.mapstruct.Mapper; import org.mapstruct.Mapping; import org.mapstruct.Named; @@ -16,44 +16,44 @@ import java.util.List; import java.util.Objects; @Mapper -public interface IotProductThingModelConvert { +public interface IotThingModelConvert { - IotProductThingModelConvert INSTANCE = Mappers.getMapper(IotProductThingModelConvert.class); + IotThingModelConvert INSTANCE = Mappers.getMapper(IotThingModelConvert.class); // 将 SaveReqVO 转换为 DO @Mapping(target = "property", expression = "java(convertToProperty(bean))") @Mapping(target = "event", expression = "java(convertToEvent(bean))") @Mapping(target = "service", expression = "java(convertToService(bean))") - IotProductThingModelDO convert(IotProductThingModelSaveReqVO bean); + IotThingModelDO convert(IotThingModelSaveReqVO bean); // 将 DO 转换为 RespVO @Mapping(target = "property", source = "property") @Mapping(target = "event", source = "event") @Mapping(target = "service", source = "service") - IotProductThingModelRespVO convert(IotProductThingModelDO bean); + IotThingModelRespVO convert(IotThingModelDO bean); // 批量转换 - List convertList(List list); + List convertList(List list); @Named("convertToProperty") - default ThingModelProperty convertToProperty(IotProductThingModelSaveReqVO bean) { - if (Objects.equals(bean.getType(), IotProductThingModelTypeEnum.PROPERTY.getType())) { + default ThingModelProperty convertToProperty(IotThingModelSaveReqVO bean) { + if (Objects.equals(bean.getType(), IotThingModelTypeEnum.PROPERTY.getType())) { return bean.getProperty(); } return null; } @Named("convertToEvent") - default ThingModelEvent convertToEvent(IotProductThingModelSaveReqVO bean) { - if (Objects.equals(bean.getType(), IotProductThingModelTypeEnum.EVENT.getType())) { + default ThingModelEvent convertToEvent(IotThingModelSaveReqVO bean) { + if (Objects.equals(bean.getType(), IotThingModelTypeEnum.EVENT.getType())) { return bean.getEvent(); } return null; } @Named("convertToService") - default ThingModelService convertToService(IotProductThingModelSaveReqVO bean) { - if (Objects.equals(bean.getType(), IotProductThingModelTypeEnum.SERVICE.getType())) { + default ThingModelService convertToService(IotThingModelSaveReqVO bean) { + if (Objects.equals(bean.getType(), IotThingModelTypeEnum.SERVICE.getType())) { return bean.getService(); } return null; diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/dataobject/device/IotDeviceDataDO.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/dataobject/device/IotDeviceDataDO.java index ab1934edb..63b732d20 100644 --- a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/dataobject/device/IotDeviceDataDO.java +++ b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/dataobject/device/IotDeviceDataDO.java @@ -1,7 +1,7 @@ package cn.iocoder.yudao.module.iot.dal.dataobject.device; import cn.iocoder.yudao.module.iot.dal.dataobject.product.IotProductDO; -import cn.iocoder.yudao.module.iot.dal.dataobject.thingmodel.IotProductThingModelDO; +import cn.iocoder.yudao.module.iot.dal.dataobject.thingmodel.IotThingModelDO; import lombok.AllArgsConstructor; import lombok.Builder; import lombok.Data; @@ -30,7 +30,7 @@ public class IotDeviceDataDO { /** * 物模型编号 *

- * 关联 {@link IotProductThingModelDO#getId()} + * 关联 {@link IotThingModelDO#getId()} */ private Long thingModelId; @@ -51,21 +51,21 @@ public class IotDeviceDataDO { /** * 属性标识符 *

- * 关联 {@link IotProductThingModelDO#getIdentifier()} + * 关联 {@link IotThingModelDO#getIdentifier()} */ private String identifier; /** * 属性名称 *

- * 关联 {@link IotProductThingModelDO#getName()} + * 关联 {@link IotThingModelDO#getName()} */ private String name; /** * 数据类型 *

- * 关联 {@link IotProductThingModelDO#getProperty()#getDataType()} + * 关联 {@link IotThingModelDO#getProperty()#getDataType()} */ private String dataType; diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/dataobject/plugininfo/PluginInfoDO.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/dataobject/plugininfo/PluginInfoDO.java index 043cdb9c8..fbdf2a1dc 100644 --- a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/dataobject/plugininfo/PluginInfoDO.java +++ b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/dataobject/plugininfo/PluginInfoDO.java @@ -1,6 +1,9 @@ package cn.iocoder.yudao.module.iot.dal.dataobject.plugininfo; import cn.iocoder.yudao.framework.mybatis.core.dataobject.BaseDO; +import cn.iocoder.yudao.module.iot.enums.plugin.IotPluginDeployTypeEnum; +import cn.iocoder.yudao.module.iot.enums.plugin.IotPluginStatusEnum; +import cn.iocoder.yudao.module.iot.enums.plugin.IotPluginTypeEnum; import com.baomidou.mybatisplus.annotation.KeySequence; import com.baomidou.mybatisplus.annotation.TableId; import com.baomidou.mybatisplus.annotation.TableName; @@ -26,11 +29,10 @@ public class PluginInfoDO extends BaseDO { */ @TableId private Long id; - // TODO @haohao:这个是不是改成类似 key 之类的字段哈? /** - * 插件包 ID + * 插件包标识符 */ - private String pluginId; + private String pluginKey; /** * 插件名称 */ @@ -41,22 +43,23 @@ public class PluginInfoDO extends BaseDO { private String description; /** * 部署方式 + *

+ * 枚举 {@link IotPluginDeployTypeEnum} */ - // TODO @haohao:枚举 private Integer deployType; /** * 插件包文件名 */ - // TODO @haohao:是不是叫 fileName 哈?避免后续有别的字段,类似 fileUrl? - private String file; + private String fileName; /** * 插件版本 */ private String version; /** * 插件类型 + *

+ * 枚举 {@link IotPluginTypeEnum} */ - // TODO @haohao:枚举 private Integer type; /** * 设备插件协议类型 @@ -64,8 +67,9 @@ public class PluginInfoDO extends BaseDO { private String protocol; /** * 状态 + *

+ * 枚举 {@link IotPluginStatusEnum} */ - // TODO @haohao:枚举 private Integer status; /** * 插件配置项描述信息 diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/dataobject/plugininstance/PluginInstanceDO.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/dataobject/plugininstance/PluginInstanceDO.java index 17f295a55..507b91f2a 100644 --- a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/dataobject/plugininstance/PluginInstanceDO.java +++ b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/dataobject/plugininstance/PluginInstanceDO.java @@ -1,12 +1,13 @@ package cn.iocoder.yudao.module.iot.dal.dataobject.plugininstance; import cn.iocoder.yudao.framework.mybatis.core.dataobject.BaseDO; +import cn.iocoder.yudao.module.iot.dal.dataobject.plugininfo.PluginInfoDO; import com.baomidou.mybatisplus.annotation.KeySequence; import com.baomidou.mybatisplus.annotation.TableId; import com.baomidou.mybatisplus.annotation.TableName; import lombok.*; -// TODO @haohao:一些必要的关联、枚举 + /** * IoT 插件实例 DO * @@ -33,6 +34,8 @@ public class PluginInstanceDO extends BaseDO { private String mainId; /** * 插件id + *

+ * 关联 {@link PluginInfoDO#getId()} */ private Long pluginId; /** diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/dataobject/tdengine/FieldParser.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/dataobject/tdengine/FieldParser.java index 9157920e0..2e4e0f507 100644 --- a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/dataobject/tdengine/FieldParser.java +++ b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/dataobject/tdengine/FieldParser.java @@ -5,7 +5,8 @@ import cn.iocoder.yudao.module.iot.controller.admin.thingmodel.model.ThingModelR import java.util.HashMap; import java.util.List; -import java.util.stream.Collectors; + +import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertList; /** * FieldParser 类用于解析和转换物模型字段到 TDengine 字段 @@ -15,6 +16,7 @@ public class FieldParser { /** * 物模型到td数据类型映射 */ + @Deprecated private static final HashMap TYPE_MAPPING = new HashMap<>() { { put("INT", "INT"); @@ -52,10 +54,7 @@ public class FieldParser { * @return 字段列表 */ public static List parse(ThingModelRespVO thingModel) { - // TODO @puhui999:是不是使用 convertList - return thingModel.getModel().getProperties().stream() - .map(FieldParser::parse) - .collect(Collectors.toList()); + return convertList(thingModel.getModel().getProperties(), FieldParser::parse); } /** @@ -65,18 +64,18 @@ public class FieldParser { * @return 转换后的 TDengine 字段对象列表 */ public static List parse(List> rows) { - // TODO @puhui999:是不是使用 convertList - return rows.stream().map(row -> { + return convertList(rows, row -> { String type = row.get(1).toString().toUpperCase(); // TODO @puhui999:"NCHAR" 最好枚举下 int dataLength = "NCHAR".equals(type) ? Integer.parseInt(row.get(2).toString()) : -1; return new TdFieldDO(row.get(0).toString(), type, dataLength); - }).collect(Collectors.toList()); + }); } /** * 获取字段字义 */ + @Deprecated public static String getFieldDefine(TdFieldDO field) { return "`" + field.getFieldName() + "`" + " " + (field.getDataLength() > 0 ? String.format("%s(%d)", field.getDataType(), field.getDataLength()) diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/dataobject/tdengine/SelectDO.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/dataobject/tdengine/SelectDO.java index 542dd1e7b..fb4cd459a 100644 --- a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/dataobject/tdengine/SelectDO.java +++ b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/dataobject/tdengine/SelectDO.java @@ -2,13 +2,12 @@ package cn.iocoder.yudao.module.iot.dal.dataobject.tdengine; import lombok.Data; -import java.util.Set; - // TODO @haohao:类似这个,其实可以参考 mybatis plus,querywrapper,搞个 TdEngineQueryWrapper。这样看起来会更好懂。 /** * 查询DO */ @Data +@Deprecated public class SelectDO { // TODO @haoha:database 是个单词 diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/dataobject/tdengine/SelectVisualDO.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/dataobject/tdengine/SelectVisualDO.java index 44acc3ac5..584562bfa 100644 --- a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/dataobject/tdengine/SelectVisualDO.java +++ b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/dataobject/tdengine/SelectVisualDO.java @@ -6,6 +6,7 @@ import java.util.Map; // TODO @haohao:类似 SelectDO 的想法,只是它是返回。ps:貌似可以在 tdengine 里面,创建一个 query 包,放这种比较特殊的查询和结果对象。dataobject 更多还是实际存储的结构化的 do @Data +@Deprecated public class SelectVisualDO { /** diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/dataobject/tdengine/TagsSelectDO.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/dataobject/tdengine/TagsSelectDO.java index 9fae91599..353830824 100644 --- a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/dataobject/tdengine/TagsSelectDO.java +++ b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/dataobject/tdengine/TagsSelectDO.java @@ -6,6 +6,7 @@ import lombok.Data; * tags查询DO */ @Data +@Deprecated public class TagsSelectDO { /** diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/dataobject/tdengine/TdFieldDO.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/dataobject/tdengine/TdFieldDO.java index 2e1751509..10c9b9fe7 100644 --- a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/dataobject/tdengine/TdFieldDO.java +++ b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/dataobject/tdengine/TdFieldDO.java @@ -5,6 +5,7 @@ import lombok.Builder; import lombok.Data; import lombok.NoArgsConstructor; +// TODO 芋艿:看看是不是后续简化掉。 /** * TD 引擎的字段 */ @@ -12,6 +13,7 @@ import lombok.NoArgsConstructor; @NoArgsConstructor @AllArgsConstructor @Builder +@Deprecated public class TdFieldDO { /** diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/dataobject/tdengine/TdResponse.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/dataobject/tdengine/TdResponse.java index aca5cece5..8b64f6854 100644 --- a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/dataobject/tdengine/TdResponse.java +++ b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/dataobject/tdengine/TdResponse.java @@ -12,6 +12,7 @@ import java.util.List; @Data @NoArgsConstructor @AllArgsConstructor +@Deprecated public class TdResponse { public static final int CODE_SUCCESS = 0; diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/dataobject/tdengine/TdRestApi.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/dataobject/tdengine/TdRestApi.java index b2d1ac029..1bb793a45 100644 --- a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/dataobject/tdengine/TdRestApi.java +++ b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/dataobject/tdengine/TdRestApi.java @@ -13,6 +13,7 @@ import org.springframework.stereotype.Service; */ @Slf4j @Service +@Deprecated // TODO 芋艿:貌似没用到 public class TdRestApi { @Value("${spring.datasource.dynamic.datasource.tdengine.url}") diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/dataobject/tdengine/TdTableDO.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/dataobject/tdengine/TdTableDO.java index 3b30352e4..3265c3ebe 100644 --- a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/dataobject/tdengine/TdTableDO.java +++ b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/dataobject/tdengine/TdTableDO.java @@ -7,6 +7,7 @@ import lombok.NoArgsConstructor; import java.util.List; +@Deprecated /** * TD 引擎的数据库 */ diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/dataobject/thingmodel/IotProductThingModelDO.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/dataobject/thingmodel/IotThingModelDO.java similarity index 77% rename from yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/dataobject/thingmodel/IotProductThingModelDO.java rename to yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/dataobject/thingmodel/IotThingModelDO.java index 3d9e5e5fe..e3b4a6d9a 100644 --- a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/dataobject/thingmodel/IotProductThingModelDO.java +++ b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/dataobject/thingmodel/IotThingModelDO.java @@ -5,7 +5,7 @@ import cn.iocoder.yudao.module.iot.controller.admin.thingmodel.model.ThingModelE import cn.iocoder.yudao.module.iot.controller.admin.thingmodel.model.ThingModelProperty; import cn.iocoder.yudao.module.iot.controller.admin.thingmodel.model.ThingModelService; import cn.iocoder.yudao.module.iot.dal.dataobject.product.IotProductDO; -import cn.iocoder.yudao.module.iot.enums.thingmodel.IotProductThingModelTypeEnum; +import cn.iocoder.yudao.module.iot.enums.thingmodel.IotThingModelTypeEnum; import com.baomidou.mybatisplus.annotation.KeySequence; import com.baomidou.mybatisplus.annotation.TableField; import com.baomidou.mybatisplus.annotation.TableId; @@ -19,17 +19,17 @@ import lombok.NoArgsConstructor; /** * IoT 产品物模型功能 DO *

- * 每个 {@link IotProductDO} 和 {@link IotProductThingModelDO} 是“一对多”的关系,它的每个属性、事件、服务都对应一条记录 + * 每个 {@link IotProductDO} 和 {@link IotThingModelDO} 是“一对多”的关系,它的每个属性、事件、服务都对应一条记录 * * @author 芋道源码 */ -@TableName(value = "iot_product_thing_model", autoResultMap = true) -@KeySequence("iot_product_thing_model_seq") // 用于 Oracle、PostgreSQL、Kingbase、DB2、H2 数据库的主键自增。如果是 MySQL 等数据库,可不写。 +@TableName(value = "iot_thing_model", autoResultMap = true) +@KeySequence("iot_thing_model_seq") // 用于 Oracle、PostgreSQL、Kingbase、DB2、H2 数据库的主键自增。如果是 MySQL 等数据库,可不写。 @Data @Builder @NoArgsConstructor @AllArgsConstructor -public class IotProductThingModelDO extends BaseDO { +public class IotThingModelDO extends BaseDO { /** * 物模型功能编号 @@ -66,7 +66,7 @@ public class IotProductThingModelDO extends BaseDO { /** * 功能类型 *

- * 枚举 {@link IotProductThingModelTypeEnum} + * 枚举 {@link IotThingModelTypeEnum} */ private Integer type; diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/mysql/device/IotDeviceMapper.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/mysql/device/IotDeviceMapper.java index 893d7eefd..2ed10eee5 100644 --- a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/mysql/device/IotDeviceMapper.java +++ b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/mysql/device/IotDeviceMapper.java @@ -6,6 +6,7 @@ import cn.iocoder.yudao.framework.mybatis.core.mapper.BaseMapperX; import cn.iocoder.yudao.framework.mybatis.core.query.LambdaQueryWrapperX; import cn.iocoder.yudao.module.iot.controller.admin.device.vo.device.IotDevicePageReqVO; import cn.iocoder.yudao.module.iot.dal.dataobject.device.IotDeviceDO; +import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; import org.apache.ibatis.annotations.Mapper; import java.util.List; @@ -47,7 +48,8 @@ public interface IotDeviceMapper extends BaseMapperX { } default IotDeviceDO selectByDeviceKey(String deviceKey) { - return selectOne(IotDeviceDO::getDeviceKey, deviceKey); + return selectOne(new LambdaQueryWrapper() + .apply("LOWER(device_key) = {0}", deviceKey.toLowerCase())); } default List selectList(Integer deviceType) { diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/mysql/plugin/PluginInfoMapper.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/mysql/plugin/PluginInfoMapper.java new file mode 100644 index 000000000..228519fb6 --- /dev/null +++ b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/mysql/plugin/PluginInfoMapper.java @@ -0,0 +1,25 @@ +package cn.iocoder.yudao.module.iot.dal.mysql.plugin; + +import cn.iocoder.yudao.framework.common.pojo.PageResult; +import cn.iocoder.yudao.framework.mybatis.core.query.LambdaQueryWrapperX; +import cn.iocoder.yudao.framework.mybatis.core.mapper.BaseMapperX; +import cn.iocoder.yudao.module.iot.dal.dataobject.plugininfo.PluginInfoDO; +import org.apache.ibatis.annotations.Mapper; +import cn.iocoder.yudao.module.iot.controller.admin.plugin.vo.*; + +/** + * IoT 插件信息 Mapper + * + * @author 芋道源码 + */ +@Mapper +public interface PluginInfoMapper extends BaseMapperX { + + default PageResult selectPage(PluginInfoPageReqVO reqVO) { + return selectPage(reqVO, new LambdaQueryWrapperX() + .likeIfPresent(PluginInfoDO::getName, reqVO.getName()) + .eqIfPresent(PluginInfoDO::getStatus, reqVO.getStatus()) + .orderByDesc(PluginInfoDO::getId)); + } + +} \ No newline at end of file diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/mysql/plugininstance/PluginInstanceMapper.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/mysql/plugin/PluginInstanceMapper.java similarity index 73% rename from yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/mysql/plugininstance/PluginInstanceMapper.java rename to yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/mysql/plugin/PluginInstanceMapper.java index 6c7f1d231..249082032 100644 --- a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/mysql/plugininstance/PluginInstanceMapper.java +++ b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/mysql/plugin/PluginInstanceMapper.java @@ -1,11 +1,11 @@ -package cn.iocoder.yudao.module.iot.dal.mysql.plugininstance; +package cn.iocoder.yudao.module.iot.dal.mysql.plugin; import cn.iocoder.yudao.framework.common.pojo.PageResult; import cn.iocoder.yudao.framework.mybatis.core.query.LambdaQueryWrapperX; import cn.iocoder.yudao.framework.mybatis.core.mapper.BaseMapperX; +import cn.iocoder.yudao.module.iot.controller.admin.plugin.vo.PluginInstancePageReqVO; import cn.iocoder.yudao.module.iot.dal.dataobject.plugininstance.PluginInstanceDO; import org.apache.ibatis.annotations.Mapper; -import cn.iocoder.yudao.module.iot.controller.admin.plugininstance.vo.*; /** * IoT 插件实例 Mapper @@ -15,6 +15,12 @@ import cn.iocoder.yudao.module.iot.controller.admin.plugininstance.vo.*; @Mapper public interface PluginInstanceMapper extends BaseMapperX { + default PluginInstanceDO selectByMainIdAndPluginId(String mainId, Long pluginId) { + return selectOne(new LambdaQueryWrapperX() + .eq(PluginInstanceDO::getMainId, mainId) + .eq(PluginInstanceDO::getPluginId, pluginId)); + } + default PageResult selectPage(PluginInstancePageReqVO reqVO) { return selectPage(reqVO, new LambdaQueryWrapperX() .eqIfPresent(PluginInstanceDO::getMainId, reqVO.getMainId()) diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/mysql/plugininfo/PluginInfoMapper.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/mysql/plugininfo/PluginInfoMapper.java deleted file mode 100644 index 69c0bd392..000000000 --- a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/mysql/plugininfo/PluginInfoMapper.java +++ /dev/null @@ -1,36 +0,0 @@ -package cn.iocoder.yudao.module.iot.dal.mysql.plugininfo; - -import cn.iocoder.yudao.framework.common.pojo.PageResult; -import cn.iocoder.yudao.framework.mybatis.core.query.LambdaQueryWrapperX; -import cn.iocoder.yudao.framework.mybatis.core.mapper.BaseMapperX; -import cn.iocoder.yudao.module.iot.dal.dataobject.plugininfo.PluginInfoDO; -import org.apache.ibatis.annotations.Mapper; -import cn.iocoder.yudao.module.iot.controller.admin.plugininfo.vo.*; - -/** - * IoT 插件信息 Mapper - * - * @author 芋道源码 - */ -@Mapper -public interface PluginInfoMapper extends BaseMapperX { - - default PageResult selectPage(PluginInfoPageReqVO reqVO) { - return selectPage(reqVO, new LambdaQueryWrapperX() - .eqIfPresent(PluginInfoDO::getPluginId, reqVO.getPluginId()) - .likeIfPresent(PluginInfoDO::getName, reqVO.getName()) - .eqIfPresent(PluginInfoDO::getDescription, reqVO.getDescription()) - .eqIfPresent(PluginInfoDO::getDeployType, reqVO.getDeployType()) - .eqIfPresent(PluginInfoDO::getFile, reqVO.getFile()) - .eqIfPresent(PluginInfoDO::getVersion, reqVO.getVersion()) - .eqIfPresent(PluginInfoDO::getType, reqVO.getType()) - .eqIfPresent(PluginInfoDO::getProtocol, reqVO.getProtocol()) - .eqIfPresent(PluginInfoDO::getStatus, reqVO.getStatus()) - .eqIfPresent(PluginInfoDO::getConfigSchema, reqVO.getConfigSchema()) - .eqIfPresent(PluginInfoDO::getConfig, reqVO.getConfig()) - .eqIfPresent(PluginInfoDO::getScript, reqVO.getScript()) - .betweenIfPresent(PluginInfoDO::getCreateTime, reqVO.getCreateTime()) - .orderByDesc(PluginInfoDO::getId)); - } - -} \ No newline at end of file diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/mysql/product/IotProductMapper.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/mysql/product/IotProductMapper.java index aef149ae7..0f5625100 100644 --- a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/mysql/product/IotProductMapper.java +++ b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/mysql/product/IotProductMapper.java @@ -5,6 +5,7 @@ import cn.iocoder.yudao.framework.mybatis.core.mapper.BaseMapperX; import cn.iocoder.yudao.framework.mybatis.core.query.LambdaQueryWrapperX; import cn.iocoder.yudao.module.iot.controller.admin.product.vo.product.IotProductPageReqVO; import cn.iocoder.yudao.module.iot.dal.dataobject.product.IotProductDO; +import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; import org.apache.ibatis.annotations.Mapper; /** @@ -23,7 +24,8 @@ public interface IotProductMapper extends BaseMapperX { } default IotProductDO selectByProductKey(String productKey) { - return selectOne(IotProductDO::getProductKey, productKey); + return selectOne(new LambdaQueryWrapper() + .apply("LOWER(product_key) = {0}", productKey.toLowerCase())); } } \ No newline at end of file diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/mysql/thingmodel/IotProductThingModelMapper.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/mysql/thingmodel/IotProductThingModelMapper.java deleted file mode 100644 index 175f66f40..000000000 --- a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/mysql/thingmodel/IotProductThingModelMapper.java +++ /dev/null @@ -1,62 +0,0 @@ -package cn.iocoder.yudao.module.iot.dal.mysql.thingmodel; - -import cn.iocoder.yudao.framework.common.pojo.PageResult; -import cn.iocoder.yudao.framework.mybatis.core.mapper.BaseMapperX; -import cn.iocoder.yudao.framework.mybatis.core.query.LambdaQueryWrapperX; -import cn.iocoder.yudao.module.iot.controller.admin.thingmodel.vo.IotProductThingModelPageReqVO; -import cn.iocoder.yudao.module.iot.dal.dataobject.thingmodel.IotProductThingModelDO; -import org.apache.ibatis.annotations.Mapper; - -import java.util.List; - -/** - * IoT 产品物模型 Mapper - * - * @author 芋道源码 - */ -@Mapper -public interface IotProductThingModelMapper extends BaseMapperX { - - default PageResult selectPage(IotProductThingModelPageReqVO reqVO) { - return selectPage(reqVO, new LambdaQueryWrapperX() - .eqIfPresent(IotProductThingModelDO::getIdentifier, reqVO.getIdentifier()) - .likeIfPresent(IotProductThingModelDO::getName, reqVO.getName()) - .eqIfPresent(IotProductThingModelDO::getType, reqVO.getType()) - .eqIfPresent(IotProductThingModelDO::getProductId, reqVO.getProductId()) - .notIn(IotProductThingModelDO::getIdentifier, "get", "set", "post") - .orderByDesc(IotProductThingModelDO::getId)); - } - - default IotProductThingModelDO selectByProductIdAndIdentifier(Long productId, String identifier) { - return selectOne(IotProductThingModelDO::getProductId, productId, - IotProductThingModelDO::getIdentifier, identifier); - } - - default List selectListByProductId(Long productId) { - return selectList(IotProductThingModelDO::getProductId, productId); - } - - default List selectListByProductIdAndType(Long productId, Integer type) { - return selectList(IotProductThingModelDO::getProductId, productId, - IotProductThingModelDO::getType, type); - } - - default List selectListByProductIdAndIdentifiersAndTypes(Long productId, - List identifiers, - List types) { - return selectList(new LambdaQueryWrapperX() - .eq(IotProductThingModelDO::getProductId, productId) - .in(IotProductThingModelDO::getIdentifier, identifiers) - .in(IotProductThingModelDO::getType, types)); - } - - default IotProductThingModelDO selectByProductIdAndName(Long productId, String name) { - return selectOne(IotProductThingModelDO::getProductId, productId, - IotProductThingModelDO::getName, name); - } - - default List selectListByProductKey(String productKey) { - return selectList(IotProductThingModelDO::getProductKey, productKey); - } - -} \ No newline at end of file diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/mysql/thingmodel/IotThingModelMapper.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/mysql/thingmodel/IotThingModelMapper.java new file mode 100644 index 000000000..b68732634 --- /dev/null +++ b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/mysql/thingmodel/IotThingModelMapper.java @@ -0,0 +1,63 @@ +package cn.iocoder.yudao.module.iot.dal.mysql.thingmodel; + +import cn.iocoder.yudao.framework.common.pojo.PageResult; +import cn.iocoder.yudao.framework.mybatis.core.mapper.BaseMapperX; +import cn.iocoder.yudao.framework.mybatis.core.query.LambdaQueryWrapperX; +import cn.iocoder.yudao.module.iot.controller.admin.thingmodel.vo.IotThingModelPageReqVO; +import cn.iocoder.yudao.module.iot.dal.dataobject.thingmodel.IotThingModelDO; +import org.apache.ibatis.annotations.Mapper; + +import java.util.List; + +/** + * IoT 产品物模型 Mapper + * + * @author 芋道源码 + */ +@Mapper +public interface IotThingModelMapper extends BaseMapperX { + + default PageResult selectPage(IotThingModelPageReqVO reqVO) { + return selectPage(reqVO, new LambdaQueryWrapperX() + .eqIfPresent(IotThingModelDO::getIdentifier, reqVO.getIdentifier()) + .likeIfPresent(IotThingModelDO::getName, reqVO.getName()) + .eqIfPresent(IotThingModelDO::getType, reqVO.getType()) + .eqIfPresent(IotThingModelDO::getProductId, reqVO.getProductId()) + // TODO @芋艿:看看要不要加枚举 + .notIn(IotThingModelDO::getIdentifier, "get", "set", "post") + .orderByDesc(IotThingModelDO::getId)); + } + + default IotThingModelDO selectByProductIdAndIdentifier(Long productId, String identifier) { + return selectOne(IotThingModelDO::getProductId, productId, + IotThingModelDO::getIdentifier, identifier); + } + + default List selectListByProductId(Long productId) { + return selectList(IotThingModelDO::getProductId, productId); + } + + default List selectListByProductIdAndType(Long productId, Integer type) { + return selectList(IotThingModelDO::getProductId, productId, + IotThingModelDO::getType, type); + } + + default List selectListByProductIdAndIdentifiersAndTypes(Long productId, + List identifiers, + List types) { + return selectList(new LambdaQueryWrapperX() + .eq(IotThingModelDO::getProductId, productId) + .in(IotThingModelDO::getIdentifier, identifiers) + .in(IotThingModelDO::getType, types)); + } + + default IotThingModelDO selectByProductIdAndName(Long productId, String name) { + return selectOne(IotThingModelDO::getProductId, productId, + IotThingModelDO::getName, name); + } + + default List selectListByProductKey(String productKey) { + return selectList(IotThingModelDO::getProductKey, productKey); + } + +} \ No newline at end of file diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/tdengine/IotDevicePropertyDataMapper.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/tdengine/IotDevicePropertyDataMapper.java new file mode 100644 index 000000000..6053444fe --- /dev/null +++ b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/tdengine/IotDevicePropertyDataMapper.java @@ -0,0 +1,77 @@ +package cn.iocoder.yudao.module.iot.dal.tdengine; + +import cn.hutool.core.collection.CollUtil; +import cn.hutool.core.util.ObjectUtil; +import cn.iocoder.yudao.module.iot.framework.tdengine.core.TDengineTableField; +import cn.iocoder.yudao.module.iot.framework.tdengine.core.annotation.TDengineDS; +import com.baomidou.mybatisplus.annotation.InterceptorIgnore; +import org.apache.ibatis.annotations.Mapper; +import org.apache.ibatis.annotations.Param; + +import java.util.ArrayList; +import java.util.List; +import java.util.stream.Collectors; + +@Mapper +@TDengineDS +@InterceptorIgnore(tenantLine = "true") // 避免 SQL 解析,因为 JSqlParser 对 TDengine 的 SQL 解析会报错 +public interface IotDevicePropertyDataMapper { + + List getProductPropertySTableFieldList(@Param("productKey") String productKey); + + void createProductPropertySTable(@Param("productKey") String productKey, + @Param("fields") List fields); + + @SuppressWarnings("SimplifyStreamApiCallChains") // 保持 JDK8 兼容性 + default void alterProductPropertySTable(String productKey, + List oldFields, + List newFields) { + oldFields.removeIf(field -> TDengineTableField.FIELD_TS.equals(field.getField()) + || TDengineTableField.FIELD_DEVICE_KEY.equals(field.getField())); + List addFields = newFields.stream().filter( // 新增的字段 + newField -> oldFields.stream().noneMatch(oldField -> oldField.getField().equals(newField.getField()))) + .collect(Collectors.toList()); + List dropFields = oldFields.stream().filter( // 删除的字段 + oldField -> newFields.stream().noneMatch(n -> n.getField().equals(oldField.getField()))) + .collect(Collectors.toList()); + List modifyTypeFields = new ArrayList<>(); // 变更类型的字段 + List modifyLengthFields = new ArrayList<>(); // 变更长度的字段 + newFields.forEach(newField -> { + TDengineTableField oldField = CollUtil.findOne(oldFields, field -> field.getField().equals(newField.getField())); + if (oldField == null) { + return; + } + if (ObjectUtil.notEqual(oldField.getType(), newField.getType())) { + modifyTypeFields.add(newField); + return; + } + if (newField.getLength()!= null) { + if (newField.getLength() > oldField.getLength()) { + modifyLengthFields.add(newField); + } else if (newField.getLength() < oldField.getLength()) { + // 特殊:TDengine 长度修改时,只允许变长,所以此时认为是修改类型 + modifyTypeFields.add(newField); + } + } + }); + + // 执行 + addFields.forEach(field -> alterProductPropertySTableAddField(productKey, field)); + dropFields.forEach(field -> alterProductPropertySTableDropField(productKey, field)); + modifyLengthFields.forEach(field -> alterProductPropertySTableModifyField(productKey, field)); + modifyTypeFields.forEach(field -> { + alterProductPropertySTableDropField(productKey, field); + alterProductPropertySTableAddField(productKey, field); + }); + } + + void alterProductPropertySTableAddField(@Param("productKey") String productKey, + @Param("field") TDengineTableField field); + + void alterProductPropertySTableModifyField(@Param("productKey") String productKey, + @Param("field") TDengineTableField field); + + void alterProductPropertySTableDropField(@Param("productKey") String productKey, + @Param("field") TDengineTableField field); + +} diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/tdengine/TdEngineDDLMapper.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/tdengine/TdEngineDDLMapper.java index 3d7aa84dd..eeb061cc5 100644 --- a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/tdengine/TdEngineDDLMapper.java +++ b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/tdengine/TdEngineDDLMapper.java @@ -96,7 +96,6 @@ public interface TdEngineDDLMapper { @TenantIgnore void modifyColumnWidthForSuperTable(TdTableDO superTable); - /** * 修改超级表 - 为超级表添加标签 * diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/emq/service/EmqxServiceImpl.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/emq/service/EmqxServiceImpl.java index 343f9ba21..46217a22b 100644 --- a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/emq/service/EmqxServiceImpl.java +++ b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/emq/service/EmqxServiceImpl.java @@ -1,7 +1,6 @@ package cn.iocoder.yudao.module.iot.emq.service; -import cn.iocoder.yudao.framework.common.util.json.JsonUtils; -import cn.iocoder.yudao.module.iot.service.device.IotDeviceDataService; +import cn.iocoder.yudao.module.iot.service.device.IotDevicePropertyDataService; import jakarta.annotation.Resource; import lombok.extern.slf4j.Slf4j; import org.eclipse.paho.client.mqttv3.MqttClient; @@ -21,7 +20,7 @@ import org.springframework.stereotype.Service; public class EmqxServiceImpl implements EmqxService { @Resource - private IotDeviceDataService iotDeviceDataService; + private IotDevicePropertyDataService iotDeviceDataService; // TODO 多线程处理消息 @Override diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/framework/plugin/ServiceRegistryConfiguration.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/framework/plugin/ServiceRegistryConfiguration.java deleted file mode 100644 index 385c8aee5..000000000 --- a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/framework/plugin/ServiceRegistryConfiguration.java +++ /dev/null @@ -1,34 +0,0 @@ -package cn.iocoder.yudao.module.iot.framework.plugin; - -import cn.iocoder.yudao.module.iot.api.DeviceDataApi; -import cn.iocoder.yudao.module.iot.api.ServiceRegistry; -import lombok.extern.slf4j.Slf4j; -import org.springframework.context.annotation.Bean; -import org.springframework.context.annotation.Configuration; - -import javax.annotation.PostConstruct; -import javax.annotation.Resource; - -@Slf4j -@Configuration -public class ServiceRegistryConfiguration { - - @Resource - private DeviceDataApi deviceDataApi; - - @PostConstruct - public void init() { - // 将主程序中的 DeviceDataApi 实例注册到 ServiceRegistry - ServiceRegistry.registerService(DeviceDataApi.class, deviceDataApi); - log.info("[init][将 DeviceDataApi 实例注册到 ServiceRegistry 中]"); - } - - /** - * 定义一个标记用的 Bean,用于表示 ServiceRegistry 已初始化完成 - */ - @Bean("serviceRegistryInitializedMarker") - public Object serviceRegistryInitializedMarker() { - // 返回任意对象即可,这里返回null都可以,但最好返回个实际对象 - return new Object(); - } -} \ No newline at end of file diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/framework/plugin/SpringConfiguration.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/framework/plugin/SpringConfiguration.java deleted file mode 100644 index d87a94f31..000000000 --- a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/framework/plugin/SpringConfiguration.java +++ /dev/null @@ -1,17 +0,0 @@ -package cn.iocoder.yudao.module.iot.framework.plugin; - -import org.pf4j.spring.SpringPluginManager; -import org.springframework.context.annotation.Bean; -import org.springframework.context.annotation.Configuration; -import org.springframework.context.annotation.DependsOn; - -@Configuration -public class SpringConfiguration { - - @Bean - @DependsOn("serviceRegistryInitializedMarker") - public SpringPluginManager pluginManager() { - return new SpringPluginManager(); - } - -} \ No newline at end of file diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/framework/plugin/UnifiedConfiguration.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/framework/plugin/UnifiedConfiguration.java new file mode 100644 index 000000000..4f1d5e3e2 --- /dev/null +++ b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/framework/plugin/UnifiedConfiguration.java @@ -0,0 +1,39 @@ +package cn.iocoder.yudao.module.iot.framework.plugin; + +import cn.iocoder.yudao.module.iot.api.ServiceRegistry; +import cn.iocoder.yudao.module.iot.api.device.DeviceDataApi; +import cn.iocoder.yudao.module.iot.framework.plugin.listener.CustomPluginStateListener; +import lombok.extern.slf4j.Slf4j; +import org.pf4j.spring.SpringPluginManager; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.springframework.context.annotation.DependsOn; + +import javax.annotation.Resource; + +@Slf4j +@Configuration +public class UnifiedConfiguration { + + private static final String SERVICE_REGISTRY_INITIALIZED_MARKER = "serviceRegistryInitializedMarker"; + + @Resource + private DeviceDataApi deviceDataApi; + + @Bean(SERVICE_REGISTRY_INITIALIZED_MARKER) + public Object serviceRegistryInitializedMarker() { + ServiceRegistry.registerService(DeviceDataApi.class, deviceDataApi); + log.info("[init][将 DeviceDataApi 实例注册到 ServiceRegistry 中]"); + return new Object(); + } + + @Bean + @DependsOn(SERVICE_REGISTRY_INITIALIZED_MARKER) + public SpringPluginManager pluginManager() { + log.info("[init][实例化 SpringPluginManager]"); + SpringPluginManager springPluginManager = new SpringPluginManager(); + springPluginManager.addPluginStateListener(new CustomPluginStateListener()); + return springPluginManager; + } + +} \ No newline at end of file diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/framework/plugin/listener/CustomPluginStateListener.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/framework/plugin/listener/CustomPluginStateListener.java new file mode 100644 index 000000000..c0802d7f5 --- /dev/null +++ b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/framework/plugin/listener/CustomPluginStateListener.java @@ -0,0 +1,24 @@ +package cn.iocoder.yudao.module.iot.framework.plugin.listener; + +import lombok.extern.slf4j.Slf4j; +import org.pf4j.PluginStateEvent; +import org.pf4j.PluginStateListener; +import org.springframework.stereotype.Component; + +@Component +@Slf4j +public class CustomPluginStateListener implements PluginStateListener { + + @Override + public void pluginStateChanged(PluginStateEvent event) { + // 1. 获取插件ID + String pluginId = event.getPlugin().getPluginId(); + // 2. 获取插件旧状态 + String oldState = event.getOldState().toString(); + // 3. 获取插件新状态 + String newState = event.getPluginState().toString(); + // 4. 打印日志信息 + log.info("插件的状态 '{}' 已更改为 '{}' 至 '{}'", pluginId, oldState, newState); + } + +} \ No newline at end of file diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/framework/tdengine/core/TDengineTableField.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/framework/tdengine/core/TDengineTableField.java new file mode 100644 index 000000000..427c68189 --- /dev/null +++ b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/framework/tdengine/core/TDengineTableField.java @@ -0,0 +1,63 @@ +package cn.iocoder.yudao.module.iot.framework.tdengine.core; + +import lombok.AllArgsConstructor; +import lombok.Data; +import lombok.NoArgsConstructor; + +/** + * TDEngine 表字段 + */ +@Data +@NoArgsConstructor +@AllArgsConstructor +public class TDengineTableField { + + /** + * 字段名 - TDengine 默认 ts 字段,默认会被 TDengine 创建 + */ + public static final String FIELD_TS = "ts"; + + /** + * 字段名 - 我们系统定义的 device_key 字段,非 TDengine 默认字段 + */ + public static final String FIELD_DEVICE_KEY = "device_key"; + + public static final String TYPE_TINYINT = "TINYINT"; + public static final String TYPE_INT = "INT"; + public static final String TYPE_FLOAT = "FLOAT"; + public static final String TYPE_DOUBLE = "DOUBLE"; + public static final String TYPE_BOOL = "BOOL"; + public static final String TYPE_NCHAR = "NCHAR"; + public static final String TYPE_TIMESTAMP = "TIMESTAMP"; + + /** + * 注释 - TAG 字段 + */ + public static final String NOTE_TAG = "TAG"; + + /** + * 字段名 + */ + private String field; + + /** + * 字段类型 + */ + private String type; + + /** + * 字段长度 + */ + private Integer length; + + /** + * 注释 + */ + private String note; + + public TDengineTableField(String field, String type) { + this.field = field; + this.type = type; + } + +} diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/framework/tdengine/core/annotation/TDengineDS.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/framework/tdengine/core/annotation/TDengineDS.java new file mode 100644 index 000000000..e3960d026 --- /dev/null +++ b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/framework/tdengine/core/annotation/TDengineDS.java @@ -0,0 +1,17 @@ +package cn.iocoder.yudao.module.iot.framework.tdengine.core.annotation; + +import com.baomidou.dynamic.datasource.annotation.DS; + +import java.lang.annotation.*; + +/** + * TDEngine 数据源 + * + * @author 芋道源码 + */ +@Target({ElementType.TYPE, ElementType.METHOD}) +@Retention(RetentionPolicy.RUNTIME) +@Documented +@DS("tdengine") +public @interface TDengineDS { +} \ No newline at end of file diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/framework/tdengine/package-info.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/framework/tdengine/package-info.java new file mode 100644 index 000000000..f92428f7b --- /dev/null +++ b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/framework/tdengine/package-info.java @@ -0,0 +1,4 @@ +/** + * iot 模块的 tdengine 拓展封装 + */ +package cn.iocoder.yudao.module.iot.framework.tdengine; \ No newline at end of file diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/job/plugin/PluginInstancesJob.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/job/plugin/PluginInstancesJob.java new file mode 100644 index 000000000..ca8398e51 --- /dev/null +++ b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/job/plugin/PluginInstancesJob.java @@ -0,0 +1,29 @@ +package cn.iocoder.yudao.module.iot.job.plugin; + + +import cn.iocoder.yudao.framework.tenant.core.util.TenantUtils; +import cn.iocoder.yudao.module.iot.service.plugin.PluginInstanceService; +import org.springframework.scheduling.annotation.Scheduled; +import org.springframework.stereotype.Component; + +import javax.annotation.Resource; +import java.util.concurrent.TimeUnit; + +/** + * 插件实例 Job + * + * @author 芋道源码 + */ +@Component +public class PluginInstancesJob { + + @Resource + private PluginInstanceService pluginInstanceService; + + @Scheduled(initialDelay = 60, fixedRate = 60, timeUnit = TimeUnit.SECONDS) + public void updatePluginInstances() { + TenantUtils.executeIgnore(() -> { + pluginInstanceService.updatePluginInstances(); + }); + } +} \ No newline at end of file diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/mq/consumer/simulatesend/SimulateSendConsumer.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/mq/consumer/simulatesend/SimulateSendConsumer.java index dfc911df5..111cf5007 100644 --- a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/mq/consumer/simulatesend/SimulateSendConsumer.java +++ b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/mq/consumer/simulatesend/SimulateSendConsumer.java @@ -1,8 +1,10 @@ package cn.iocoder.yudao.module.iot.mq.consumer.simulatesend; /** + * TODO @alwayssuper:记得实现,还有类注释哈 + * * @author alwayssuper - * @date 2024/12/20 8:04 + * @since 2024/12/20 8:04 */ public class SimulateSendConsumer { } diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/device/IotDeviceDataService.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/device/IotDevicePropertyDataService.java similarity index 71% rename from yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/device/IotDeviceDataService.java rename to yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/device/IotDevicePropertyDataService.java index 1dd4ad666..08375cb09 100644 --- a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/device/IotDeviceDataService.java +++ b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/device/IotDevicePropertyDataService.java @@ -9,11 +9,18 @@ import java.util.List; import java.util.Map; /** - * IoT 设备数据 Service 接口 + * IoT 设备【属性】数据 Service 接口 * * @author 芋道源码 */ -public interface IotDeviceDataService { +public interface IotDevicePropertyDataService { + + /** + * 定义设备属性数据的结构 + * + * @param productId 产品编号 + */ + void defineDevicePropertyData(Long productId); /** * 保存设备数据 @@ -21,7 +28,7 @@ public interface IotDeviceDataService { * @param productKey 产品 key * @param deviceName 设备名称 * @param message 消息 - *

JSON 格式,参见 ... + *

参见 JSON 格式 */ void saveDeviceData(String productKey, String deviceName, String message); @@ -40,4 +47,5 @@ public interface IotDeviceDataService { * @return 设备属性历史数据 */ PageResult> getHistoryDeviceProperties(@Valid IotDeviceDataPageReqVO deviceDataReqVO); + } \ No newline at end of file diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/device/IotDeviceDataServiceImpl.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/device/IotDevicePropertyDataServiceImpl.java similarity index 56% rename from yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/device/IotDeviceDataServiceImpl.java rename to yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/device/IotDevicePropertyDataServiceImpl.java index 4cb39c0bc..eb7fcd430 100644 --- a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/device/IotDeviceDataServiceImpl.java +++ b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/device/IotDevicePropertyDataServiceImpl.java @@ -1,21 +1,29 @@ package cn.iocoder.yudao.module.iot.service.device; +import cn.hutool.core.collection.CollUtil; import cn.hutool.core.date.DateUtil; +import cn.hutool.core.map.MapUtil; import cn.hutool.core.util.StrUtil; import cn.hutool.json.JSONObject; import cn.iocoder.yudao.framework.common.pojo.PageResult; import cn.iocoder.yudao.module.iot.controller.admin.device.vo.deviceData.IotDeviceDataPageReqVO; +import cn.iocoder.yudao.module.iot.controller.admin.thingmodel.model.dataType.ThingModelDateOrTextDataSpecs; import cn.iocoder.yudao.module.iot.dal.dataobject.device.IotDeviceDO; import cn.iocoder.yudao.module.iot.dal.dataobject.device.IotDeviceDataDO; +import cn.iocoder.yudao.module.iot.dal.dataobject.product.IotProductDO; import cn.iocoder.yudao.module.iot.dal.dataobject.tdengine.SelectVisualDO; import cn.iocoder.yudao.module.iot.dal.dataobject.tdengine.ThingModelMessage; -import cn.iocoder.yudao.module.iot.dal.dataobject.thingmodel.IotProductThingModelDO; +import cn.iocoder.yudao.module.iot.dal.dataobject.thingmodel.IotThingModelDO; +import cn.iocoder.yudao.module.iot.dal.tdengine.IotDevicePropertyDataMapper; import cn.iocoder.yudao.module.iot.dal.redis.deviceData.DeviceDataRedisDAO; import cn.iocoder.yudao.module.iot.dal.tdengine.TdEngineDMLMapper; import cn.iocoder.yudao.module.iot.enums.IotConstants; -import cn.iocoder.yudao.module.iot.enums.thingmodel.IotProductThingModelTypeEnum; +import cn.iocoder.yudao.module.iot.enums.thingmodel.IotDataSpecsDataTypeEnum; +import cn.iocoder.yudao.module.iot.enums.thingmodel.IotThingModelTypeEnum; +import cn.iocoder.yudao.module.iot.framework.tdengine.core.TDengineTableField; +import cn.iocoder.yudao.module.iot.service.product.IotProductService; import cn.iocoder.yudao.module.iot.service.tdengine.IotThingModelMessageService; -import cn.iocoder.yudao.module.iot.service.thingmodel.IotProductThingModelService; +import cn.iocoder.yudao.module.iot.service.thingmodel.IotThingModelService; import jakarta.annotation.Resource; import jakarta.validation.Valid; import lombok.extern.slf4j.Slf4j; @@ -28,11 +36,32 @@ import java.util.HashMap; import java.util.List; import java.util.Map; +import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertList; import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.filterList; -@Slf4j +/** + * IoT 设备【属性】数据 Service 实现类 + * + * @author 芋道源码 + */ @Service -public class IotDeviceDataServiceImpl implements IotDeviceDataService { +@Slf4j +public class IotDevicePropertyDataServiceImpl implements IotDevicePropertyDataService { + + /** + * 物模型的数据类型,与 TDengine 数据类型的映射关系 + */ + private static final Map TYPE_MAPPING = MapUtil.builder() + .put(IotDataSpecsDataTypeEnum.INT.getDataType(), TDengineTableField.TYPE_INT) + .put(IotDataSpecsDataTypeEnum.FLOAT.getDataType(), TDengineTableField.TYPE_FLOAT) + .put(IotDataSpecsDataTypeEnum.DOUBLE.getDataType(), TDengineTableField.TYPE_DOUBLE) + .put(IotDataSpecsDataTypeEnum.ENUM.getDataType(), TDengineTableField.TYPE_TINYINT) // TODO 芋艿:为什么要映射为 TINYINT 的说明? + .put( IotDataSpecsDataTypeEnum.BOOL.getDataType(), TDengineTableField.TYPE_TINYINT) // TODO 芋艿:为什么要映射为 TINYINT 的说明? + .put(IotDataSpecsDataTypeEnum.TEXT.getDataType(), TDengineTableField.TYPE_NCHAR) + .put(IotDataSpecsDataTypeEnum.DATE.getDataType(), TDengineTableField.TYPE_TIMESTAMP) + .put(IotDataSpecsDataTypeEnum.STRUCT.getDataType(), TDengineTableField.TYPE_NCHAR) // TODO 芋艿:怎么映射!!!! + .put(IotDataSpecsDataTypeEnum.ARRAY.getDataType(), TDengineTableField.TYPE_NCHAR) // TODO 芋艿:怎么映射!!!! + .build(); @Value("${spring.datasource.dynamic.datasource.tdengine.url}") private String url; @@ -42,13 +71,62 @@ public class IotDeviceDataServiceImpl implements IotDeviceDataService { @Resource private IotThingModelMessageService thingModelMessageService; @Resource - private IotProductThingModelService thingModelService; + private IotThingModelService thingModelService; + @Resource + private IotProductService productService; + @Resource private TdEngineDMLMapper tdEngineDMLMapper; @Resource private DeviceDataRedisDAO deviceDataRedisDAO; + @Resource + private IotDevicePropertyDataMapper devicePropertyDataMapper; + + @Override + public void defineDevicePropertyData(Long productId) { + // 1.1 查询产品和物模型 + IotProductDO product = productService.validateProductExists(productId); + List thingModels = filterList(thingModelService.getThingModelListByProductId(productId), + thingModel -> IotThingModelTypeEnum.PROPERTY.getType().equals(thingModel.getType())); + // 1.2 解析 DB 里的字段 + List oldFields = new ArrayList<>(); + try { + oldFields.addAll(devicePropertyDataMapper.getProductPropertySTableFieldList(product.getProductKey())); + } catch (Exception e) { + if (!e.getMessage().contains("Table does not exist")) { + throw e; + } + } + + // 2.1 情况一:如果是新增的时候,需要创建表 + List newFields = buildTableFieldList(thingModels); + if (CollUtil.isEmpty(oldFields)) { + if (CollUtil.isEmpty(newFields)) { + log.info("[defineDevicePropertyData][productId({}) 没有需要定义的属性]", productId); + return; + } + newFields.add(0, new TDengineTableField(TDengineTableField.FIELD_TS, TDengineTableField.TYPE_TIMESTAMP)); + devicePropertyDataMapper.createProductPropertySTable(product.getProductKey(), newFields); + return; + } + // 2.2 情况二:如果是修改的时候,需要更新表 + devicePropertyDataMapper.alterProductPropertySTable(product.getProductKey(), oldFields, newFields); + } + + private List buildTableFieldList(List thingModels) { + return convertList(thingModels, thingModel -> { + TDengineTableField field = new TDengineTableField( + StrUtil.toUnderlineCase(thingModel.getIdentifier()), // TDengine 字段默认都是小写 + TYPE_MAPPING.get(thingModel.getProperty().getDataType())); + if (thingModel.getProperty().getDataType().equals(IotDataSpecsDataTypeEnum.TEXT.getDataType())) { + field.setLength(((ThingModelDateOrTextDataSpecs) thingModel.getProperty().getDataSpecs()).getLength()); + } + return field; + }); + } + @Override public void saveDeviceData(String productKey, String deviceName, String message) { // 1. 根据产品 key 和设备名称,获得设备信息 @@ -75,8 +153,8 @@ public class IotDeviceDataServiceImpl implements IotDeviceDataService { // 1. 获取设备信息 IotDeviceDO device = deviceService.getDevice(deviceDataReqVO.getDeviceId()); // 2. 获取设备属性最新数据 - List thingModelList = thingModelService.getProductThingModelListByProductKey(device.getProductKey()); - thingModelList = filterList(thingModelList, thingModel -> IotProductThingModelTypeEnum.PROPERTY.getType() + List thingModelList = thingModelService.getProductThingModelListByProductKey(device.getProductKey()); + thingModelList = filterList(thingModelList, thingModel -> IotThingModelTypeEnum.PROPERTY.getType() .equals(thingModel.getType())); // 3. 过滤标识符和属性名称 diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/device/IotDeviceServiceImpl.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/device/IotDeviceServiceImpl.java index a43521ff9..1896b98b7 100644 --- a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/device/IotDeviceServiceImpl.java +++ b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/device/IotDeviceServiceImpl.java @@ -9,6 +9,7 @@ import cn.iocoder.yudao.framework.common.pojo.PageResult; import cn.iocoder.yudao.framework.common.util.object.BeanUtils; import cn.iocoder.yudao.framework.common.util.validation.ValidationUtils; import cn.iocoder.yudao.framework.tenant.core.aop.TenantIgnore; +import cn.iocoder.yudao.framework.tenant.core.util.TenantUtils; import cn.iocoder.yudao.module.iot.controller.admin.device.vo.device.*; import cn.iocoder.yudao.module.iot.dal.dataobject.device.IotDeviceDO; import cn.iocoder.yudao.module.iot.dal.dataobject.device.IotDeviceGroupDO; @@ -60,9 +61,11 @@ public class IotDeviceServiceImpl implements IotDeviceService { throw exception(PRODUCT_NOT_EXISTS); } // 1.2 校验设备标识是否唯一 - if (deviceMapper.selectByDeviceKey(createReqVO.getDeviceKey()) != null) { - throw exception(DEVICE_KEY_EXISTS); - } + TenantUtils.executeIgnore(() -> { + if (deviceMapper.selectByDeviceKey(createReqVO.getDeviceKey()) != null) { + throw exception(PRODUCT_KEY_EXISTS); + } + }); // 1.3 校验设备名称在同一产品下是否唯一 if (deviceMapper.selectByProductKeyAndDeviceName(product.getProductKey(), createReqVO.getDeviceKey()) != null) { throw exception(DEVICE_NAME_EXISTS); diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/plugininfo/PluginInfoService.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/plugin/PluginInfoService.java similarity index 77% rename from yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/plugininfo/PluginInfoService.java rename to yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/plugin/PluginInfoService.java index 867103402..56b7e95e1 100644 --- a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/plugininfo/PluginInfoService.java +++ b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/plugin/PluginInfoService.java @@ -1,12 +1,14 @@ -package cn.iocoder.yudao.module.iot.service.plugininfo; +package cn.iocoder.yudao.module.iot.service.plugin; import cn.iocoder.yudao.framework.common.pojo.PageResult; -import cn.iocoder.yudao.module.iot.controller.admin.plugininfo.vo.PluginInfoPageReqVO; -import cn.iocoder.yudao.module.iot.controller.admin.plugininfo.vo.PluginInfoSaveReqVO; +import cn.iocoder.yudao.module.iot.controller.admin.plugin.vo.PluginInfoPageReqVO; +import cn.iocoder.yudao.module.iot.controller.admin.plugin.vo.PluginInfoSaveReqVO; import cn.iocoder.yudao.module.iot.dal.dataobject.plugininfo.PluginInfoDO; import jakarta.validation.Valid; import org.springframework.web.multipart.MultipartFile; +import java.util.List; + /** * IoT 插件信息 Service 接口 * @@ -58,7 +60,7 @@ public interface PluginInfoService { * @param id 插件id * @param file 文件 */ - void uploadJar(Long id, MultipartFile file); + void uploadFile(Long id, MultipartFile file); /** * 更新插件的状态 @@ -67,4 +69,11 @@ public interface PluginInfoService { * @param status 状态 */ void updatePluginStatus(Long id, Integer status); + + /** + * 获得插件信息列表 + * + * @return 插件信息列表 + */ + List getPluginInfoList(); } \ No newline at end of file diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/plugin/PluginInfoServiceImpl.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/plugin/PluginInfoServiceImpl.java new file mode 100644 index 000000000..4e7f2e996 --- /dev/null +++ b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/plugin/PluginInfoServiceImpl.java @@ -0,0 +1,270 @@ +package cn.iocoder.yudao.module.iot.service.plugin; + +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.plugin.vo.PluginInfoPageReqVO; +import cn.iocoder.yudao.module.iot.controller.admin.plugin.vo.PluginInfoSaveReqVO; +import cn.iocoder.yudao.module.iot.dal.dataobject.plugininfo.PluginInfoDO; +import cn.iocoder.yudao.module.iot.dal.mysql.plugin.PluginInfoMapper; +import cn.iocoder.yudao.module.iot.enums.plugin.IotPluginStatusEnum; +import jakarta.annotation.Resource; +import lombok.extern.slf4j.Slf4j; +import org.pf4j.PluginDescriptor; +import org.pf4j.PluginState; +import org.pf4j.PluginWrapper; +import org.pf4j.spring.SpringPluginManager; +import org.springframework.beans.factory.annotation.Value; +import org.springframework.stereotype.Service; +import org.springframework.validation.annotation.Validated; +import org.springframework.web.multipart.MultipartFile; + +import java.io.File; +import java.io.IOException; +import java.nio.file.*; +import java.util.ArrayList; +import java.util.List; +import java.util.concurrent.Executors; +import java.util.concurrent.TimeUnit; + +import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception; +import static cn.iocoder.yudao.module.iot.enums.ErrorCodeConstants.*; + +/** + * IoT 插件信息 Service 实现类 + * + * @author 芋道源码 + */ +@Service +@Validated +@Slf4j +public class PluginInfoServiceImpl implements PluginInfoService { + + @Resource + private PluginInfoMapper pluginInfoMapper; + @Resource + private SpringPluginManager pluginManager; + + @Value("${pf4j.pluginsDir}") + private String pluginsDir; + + @Override + public Long createPluginInfo(PluginInfoSaveReqVO createReqVO) { + // 插入 + PluginInfoDO pluginInfo = BeanUtils.toBean(createReqVO, PluginInfoDO.class); + pluginInfoMapper.insert(pluginInfo); + // 返回 + return pluginInfo.getId(); + } + + @Override + public void updatePluginInfo(PluginInfoSaveReqVO updateReqVO) { + // 校验存在 + validatePluginInfoExists(updateReqVO.getId()); + // 更新 + PluginInfoDO updateObj = BeanUtils.toBean(updateReqVO, PluginInfoDO.class); + pluginInfoMapper.updateById(updateObj); + } + + @Override + public void deletePluginInfo(Long id) { + // 校验存在 + PluginInfoDO pluginInfoDO = validatePluginInfoExists(id); + + // 停止插件 + if (IotPluginStatusEnum.RUNNING.getStatus().equals(pluginInfoDO.getStatus())) { + throw exception(PLUGIN_INFO_DELETE_FAILED_RUNNING); + } + + // 卸载插件 + PluginWrapper plugin = pluginManager.getPlugin(pluginInfoDO.getPluginKey()); + if (plugin != null) { + // 查询插件是否是启动状态 + if (plugin.getPluginState().equals(PluginState.STARTED)) { + // 停止插件 + pluginManager.stopPlugin(plugin.getPluginId()); + } + // 卸载插件 + pluginManager.unloadPlugin(plugin.getPluginId()); + } + + // 删除 + pluginInfoMapper.deleteById(id); + // 删除插件文件 + Executors.newSingleThreadExecutor().submit(() -> { + try { + TimeUnit.SECONDS.sleep(1); // 等待 1 秒,避免插件未卸载完毕 + File file = new File(pluginsDir, pluginInfoDO.getFileName()); + if (file.exists() && !file.delete()) { + log.error("[deletePluginInfo][删除插件文件({}) 失败]", pluginInfoDO.getFileName()); + } + } catch (InterruptedException e) { + log.error("[deletePluginInfo][删除插件文件({}) 失败]", pluginInfoDO.getFileName(), e); + } + }); + + } + + private PluginInfoDO validatePluginInfoExists(Long id) { + PluginInfoDO pluginInfo = pluginInfoMapper.selectById(id); + if (pluginInfo == null) { + throw exception(PLUGIN_INFO_NOT_EXISTS); + } + return pluginInfo; + } + + @Override + public PluginInfoDO getPluginInfo(Long id) { + return pluginInfoMapper.selectById(id); + } + + @Override + public PageResult getPluginInfoPage(PluginInfoPageReqVO pageReqVO) { + return pluginInfoMapper.selectPage(pageReqVO); + } + + @Override + public void uploadFile(Long id, MultipartFile file) { + // 1. 校验插件信息是否存在 + PluginInfoDO pluginInfoDo = validatePluginInfoExists(id); + + // 2. 获取插件标识 + String pluginKey = pluginInfoDo.getPluginKey(); + + // 3. 停止并卸载旧的插件 + stopAndUnloadPlugin(pluginKey); + + // 4. 上传新的插件文件 + String pluginKeyNew = uploadAndLoadNewPlugin(file); + + // 5. 更新插件启用状态文件 + updatePluginStatusFile(pluginKeyNew, false); + + // 6. 更新插件信息 + updatePluginInfo(pluginInfoDo, pluginKeyNew, file); + } + + // 停止并卸载旧的插件 + private void stopAndUnloadPlugin(String pluginKey) { + PluginWrapper plugin = pluginManager.getPlugin(pluginKey); + if (plugin != null) { + if (plugin.getPluginState().equals(PluginState.STARTED)) { + pluginManager.stopPlugin(pluginKey); // 停止插件 + } + pluginManager.unloadPlugin(pluginKey); // 卸载插件 + } + } + + // 上传并加载新的插件文件 + private String uploadAndLoadNewPlugin(MultipartFile file) { + Path pluginsPath = Paths.get(pluginsDir); + try { + if (!Files.exists(pluginsPath)) { + Files.createDirectories(pluginsPath); // 创建插件目录 + } + String filename = file.getOriginalFilename(); + if (filename != null) { + Path jarPath = pluginsPath.resolve(filename); + Files.copy(file.getInputStream(), jarPath, StandardCopyOption.REPLACE_EXISTING); // 保存上传的 JAR 文件 + return pluginManager.loadPlugin(jarPath.toAbsolutePath()); // 加载插件 + } else { + throw exception(PLUGIN_INSTALL_FAILED); + } + } catch (Exception e) { + throw exception(PLUGIN_INSTALL_FAILED); + } + } + + // 更新插件状态文件 + private void updatePluginStatusFile(String pluginKeyNew, boolean isEnabled) { + Path enabledFilePath = Paths.get(pluginsDir, "enabled.txt"); + Path disabledFilePath = Paths.get(pluginsDir, "disabled.txt"); + Path targetFilePath = isEnabled ? enabledFilePath : disabledFilePath; + Path oppositeFilePath = isEnabled ? disabledFilePath : enabledFilePath; + + try { + PluginWrapper pluginWrapper = pluginManager.getPlugin(pluginKeyNew); + if (pluginWrapper == null) { + throw exception(PLUGIN_INSTALL_FAILED); + } + String pluginInfo = pluginKeyNew + "@" + pluginWrapper.getDescriptor().getVersion(); + List targetLines = Files.exists(targetFilePath) ? Files.readAllLines(targetFilePath) + : new ArrayList<>(); + List oppositeLines = Files.exists(oppositeFilePath) ? Files.readAllLines(oppositeFilePath) + : new ArrayList<>(); + + if (!targetLines.contains(pluginInfo)) { + targetLines.add(pluginInfo); + Files.write(targetFilePath, targetLines, StandardOpenOption.CREATE, + StandardOpenOption.TRUNCATE_EXISTING); + } + + if (oppositeLines.contains(pluginInfo)) { + oppositeLines.remove(pluginInfo); + Files.write(oppositeFilePath, oppositeLines, StandardOpenOption.CREATE, + StandardOpenOption.TRUNCATE_EXISTING); + } + } catch (IOException e) { + throw exception(PLUGIN_INSTALL_FAILED); + } + } + + // 更新插件信息 + private void updatePluginInfo(PluginInfoDO pluginInfoDo, String pluginKeyNew, MultipartFile file) { + pluginInfoDo.setPluginKey(pluginKeyNew); + pluginInfoDo.setStatus(IotPluginStatusEnum.STOPPED.getStatus()); + pluginInfoDo.setFileName(file.getOriginalFilename()); + pluginInfoDo.setScript(""); + + PluginDescriptor pluginDescriptor = pluginManager.getPlugin(pluginKeyNew).getDescriptor(); + pluginInfoDo.setConfigSchema(pluginDescriptor.getPluginDescription()); + pluginInfoDo.setVersion(pluginDescriptor.getVersion()); + pluginInfoDo.setDescription(pluginDescriptor.getPluginDescription()); + pluginInfoMapper.updateById(pluginInfoDo); + } + + @Override + public void updatePluginStatus(Long id, Integer status) { + // 1. 校验插件信息是否存在 + PluginInfoDO pluginInfoDo = validatePluginInfoExists(id); + + // 2. 校验插件状态是否有效 + if (!IotPluginStatusEnum.contains(status)) { + throw exception(PLUGIN_STATUS_INVALID); + } + + // 3. 获取插件标识和插件实例 + String pluginKey = pluginInfoDo.getPluginKey(); + PluginWrapper plugin = pluginManager.getPlugin(pluginKey); + + // 4. 根据状态更新插件 + if (plugin != null) { + // 4.1 如果目标状态是运行且插件未启动,则启动插件 + if (status.equals(IotPluginStatusEnum.RUNNING.getStatus()) + && plugin.getPluginState() != PluginState.STARTED) { + pluginManager.startPlugin(pluginKey); + updatePluginStatusFile(pluginKey, true); // 更新插件状态文件为启用 + } + // 4.2 如果目标状态是停止且插件已启动,则停止插件 + else if (status.equals(IotPluginStatusEnum.STOPPED.getStatus()) + && plugin.getPluginState() == PluginState.STARTED) { + pluginManager.stopPlugin(pluginKey); + updatePluginStatusFile(pluginKey, false); // 更新插件状态文件为禁用 + } + } else { + // 5. 插件不存在且状态为停止,抛出异常 + if (IotPluginStatusEnum.STOPPED.getStatus().equals(pluginInfoDo.getStatus())) { + throw exception(PLUGIN_STATUS_INVALID); + } + } + + // 6. 更新数据库中的插件状态 + pluginInfoDo.setStatus(status); + pluginInfoMapper.updateById(pluginInfoDo); + } + + @Override + public List getPluginInfoList() { + return pluginInfoMapper.selectList(null); + } + +} \ No newline at end of file diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/plugin/PluginInstanceService.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/plugin/PluginInstanceService.java new file mode 100644 index 000000000..5655f1d3a --- /dev/null +++ b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/plugin/PluginInstanceService.java @@ -0,0 +1,15 @@ +package cn.iocoder.yudao.module.iot.service.plugin; + +/** + * IoT 插件实例 Service 接口 + * + * @author 芋道源码 + */ +public interface PluginInstanceService { + + /** + * 更新IoT 插件实例 + */ + void updatePluginInstances(); + +} \ No newline at end of file diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/plugin/PluginInstanceServiceImpl.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/plugin/PluginInstanceServiceImpl.java new file mode 100644 index 000000000..52d79207b --- /dev/null +++ b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/plugin/PluginInstanceServiceImpl.java @@ -0,0 +1,100 @@ +package cn.iocoder.yudao.module.iot.service.plugin; + +import cn.hutool.core.net.NetUtil; +import cn.hutool.core.util.IdUtil; +import cn.iocoder.yudao.module.iot.dal.dataobject.plugininfo.PluginInfoDO; +import cn.iocoder.yudao.module.iot.dal.dataobject.plugininstance.PluginInstanceDO; +import cn.iocoder.yudao.module.iot.dal.mysql.plugin.PluginInstanceMapper; +import jakarta.annotation.Resource; +import lombok.extern.slf4j.Slf4j; +import org.pf4j.PluginWrapper; +import org.pf4j.spring.SpringPluginManager; +import org.springframework.beans.factory.annotation.Value; +import org.springframework.stereotype.Service; +import org.springframework.validation.annotation.Validated; + +import java.util.List; + +/** + * IoT 插件实例 Service 实现类 + * + * @author 芋道源码 + */ +@Service +@Validated +@Slf4j +public class PluginInstanceServiceImpl implements PluginInstanceService { + + /** + * 主程序id + */ + public static final String MAIN_ID = IdUtil.fastSimpleUUID(); + + @Resource + private PluginInfoService pluginInfoService; + @Resource + private PluginInstanceMapper pluginInstanceMapper; + @Resource + private SpringPluginManager pluginManager; + + @Value("${server.port:48080}") + private int port; + + + @Override + public void updatePluginInstances() { + // 1. 查询 pf4j 插件列表 + List plugins = pluginManager.getPlugins(); + + // 2. 查询插件信息列表 + List pluginInfos = pluginInfoService.getPluginInfoList(); + + // 动态获取主程序的 IP 和端口 + String mainIp = getLocalIpAddress(); + + // 3. 遍历插件列表,并保存为插件实例 + for (PluginWrapper plugin : plugins) { + String pluginKey = plugin.getPluginId(); + PluginInfoDO pluginInfo = pluginInfos.stream() + .filter(pluginInfoDO -> pluginInfoDO.getPluginKey().equals(pluginKey)) + .findFirst() + .orElse(null); + + // 4. 如果插件信息不存在,则跳过 + if (pluginInfo == null) { + continue; + } + + // 5. 查询插件实例 + PluginInstanceDO pluginInstance = pluginInstanceMapper.selectByMainIdAndPluginId(MAIN_ID, pluginInfo.getId()); + + // 6. 如果插件实例不存在,则创建 + if (pluginInstance == null) { + pluginInstance = new PluginInstanceDO(); + pluginInstance.setPluginId(pluginInfo.getId()); + pluginInstance.setMainId(MAIN_ID); + pluginInstance.setIp(mainIp); + pluginInstance.setPort(port); + pluginInstance.setHeartbeatAt(System.currentTimeMillis()); + pluginInstanceMapper.insert(pluginInstance); + } else { + // 7. 如果插件实例存在,则更新 + pluginInstance.setHeartbeatAt(System.currentTimeMillis()); + pluginInstanceMapper.updateById(pluginInstance); + } + } + } + + private String getLocalIpAddress() { + try { + List ipList = NetUtil.localIpv4s().stream() + .filter(ip -> !ip.startsWith("0.0") && !ip.startsWith("127.") && !ip.startsWith("169.254") && !ip.startsWith("255.255.255.255")) + .toList(); + return ipList.isEmpty() ? "127.0.0.1" : ipList.get(0); + } catch (Exception e) { + log.error("获取本地IP地址失败", e); + return "127.0.0.1"; // 默认值 + } + } + +} \ No newline at end of file diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/plugininfo/PluginInfoServiceImpl.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/plugininfo/PluginInfoServiceImpl.java deleted file mode 100644 index bd7efa8d0..000000000 --- a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/plugininfo/PluginInfoServiceImpl.java +++ /dev/null @@ -1,241 +0,0 @@ -package cn.iocoder.yudao.module.iot.service.plugininfo; - -import cn.hutool.core.io.IoUtil; -import cn.iocoder.yudao.framework.common.pojo.PageResult; -import cn.iocoder.yudao.framework.common.util.object.BeanUtils; -import cn.iocoder.yudao.module.infra.api.file.FileApi; -import cn.iocoder.yudao.module.iot.controller.admin.plugininfo.vo.PluginInfoPageReqVO; -import cn.iocoder.yudao.module.iot.controller.admin.plugininfo.vo.PluginInfoSaveReqVO; -import cn.iocoder.yudao.module.iot.dal.dataobject.plugininfo.PluginInfoDO; -import cn.iocoder.yudao.module.iot.dal.mysql.plugininfo.PluginInfoMapper; -import cn.iocoder.yudao.module.iot.enums.plugin.IotPluginStatusEnum; -import jakarta.annotation.PostConstruct; -import jakarta.annotation.Resource; -import lombok.SneakyThrows; -import lombok.extern.slf4j.Slf4j; -import org.pf4j.PluginDescriptor; -import org.pf4j.PluginState; -import org.pf4j.PluginWrapper; -import org.pf4j.spring.SpringPluginManager; -import org.springframework.beans.factory.annotation.Value; -import org.springframework.stereotype.Service; -import org.springframework.validation.annotation.Validated; -import org.springframework.web.multipart.MultipartFile; - -import java.nio.file.Path; -import java.util.concurrent.Executors; -import java.util.concurrent.TimeUnit; -import java.util.jar.JarEntry; -import java.util.jar.JarFile; - -import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception; -import static cn.iocoder.yudao.module.iot.enums.ErrorCodeConstants.*; - -/** - * IoT 插件信息 Service 实现类 - * - * @author 芋道源码 - */ -@Service -@Validated -@Slf4j -public class PluginInfoServiceImpl implements PluginInfoService { - - @Resource - private PluginInfoMapper pluginInfoMapper; - - @Resource - private SpringPluginManager pluginManager; - - @Resource - private FileApi fileApi; - - @Value("${pf4j.pluginsDir}") - private String pluginsDir; - - @Override - public Long createPluginInfo(PluginInfoSaveReqVO createReqVO) { - // 插入 - PluginInfoDO pluginInfo = BeanUtils.toBean(createReqVO, PluginInfoDO.class); - pluginInfoMapper.insert(pluginInfo); - // 返回 - return pluginInfo.getId(); - } - - @Override - public void updatePluginInfo(PluginInfoSaveReqVO updateReqVO) { - // 校验存在 - validatePluginInfoExists(updateReqVO.getId()); - // 更新 - PluginInfoDO updateObj = BeanUtils.toBean(updateReqVO, PluginInfoDO.class); - pluginInfoMapper.updateById(updateObj); - } - - @Override - public void deletePluginInfo(Long id) { - // 校验存在 - PluginInfoDO pluginInfoDO = validatePluginInfoExists(id); - - // 停止插件 - if (IotPluginStatusEnum.RUNNING.getStatus().equals(pluginInfoDO.getStatus())) { - throw exception(PLUGIN_INFO_DELETE_FAILED_RUNNING); - } - - // 卸载插件 - PluginWrapper plugin = pluginManager.getPlugin(pluginInfoDO.getPluginId()); - if (plugin != null) { - // 查询插件是否是启动状态 - if (plugin.getPluginState().equals(PluginState.STARTED)) { - // 停止插件 - pluginManager.stopPlugin(plugin.getPluginId()); - } - // 卸载插件 - pluginManager.unloadPlugin(plugin.getPluginId()); - } - - // 删除 - pluginInfoMapper.deleteById(id); - } - - private PluginInfoDO validatePluginInfoExists(Long id) { - PluginInfoDO pluginInfo = pluginInfoMapper.selectById(id); - if (pluginInfo == null) { - throw exception(PLUGIN_INFO_NOT_EXISTS); - } - return pluginInfo; - } - - @Override - public PluginInfoDO getPluginInfo(Long id) { - return pluginInfoMapper.selectById(id); - } - - @Override - public PageResult getPluginInfoPage(PluginInfoPageReqVO pageReqVO) { - return pluginInfoMapper.selectPage(pageReqVO); - } - - @Override - public void uploadJar(Long id, MultipartFile file) { - // 1. 校验存在 - PluginInfoDO pluginInfoDo = validatePluginInfoExists(id); - - // 2. 判断文件名称与插件 ID 是否匹配 - String pluginId = pluginInfoDo.getPluginId(); - - // 3. 停止卸载旧的插件 - // 3.1. 获取插件信息 - PluginWrapper plugin = pluginManager.getPlugin(pluginId); - if (plugin != null) { - // 3.2. 如果插件状态是启动的,停止插件 - if (plugin.getPluginState().equals(PluginState.STARTED)) { - pluginManager.stopPlugin(pluginId); - } - // 3.3. 卸载插件 - pluginManager.unloadPlugin(pluginId); - } - - // 4. 上传插件 - String pluginIdNew; - try { - String path = fileApi.createFile(pluginsDir, IoUtil.readBytes(file.getInputStream())); - Path pluginPath = Path.of(path); - pluginIdNew = pluginManager.loadPlugin(pluginPath); - } catch (Exception e) { - throw exception(PLUGIN_INSTALL_FAILED); - } - - PluginWrapper pluginWrapper = pluginManager.getPlugin(pluginIdNew); - if (pluginWrapper == null) { - throw exception(PLUGIN_INSTALL_FAILED); - } - - // 5. 读取配置文件和脚本 - String configJson = ""; - String script = ""; - try (JarFile jarFile = new JarFile(pluginWrapper.getPluginPath().toFile())) { - // 5.1 获取config文件在jar包中的路径 - String configFile = "classes/config.json"; - JarEntry configEntry = jarFile.getJarEntry(configFile); - - if (configEntry != null) { - // 5.2 读取配置文件 - configJson = IoUtil.readUtf8(jarFile.getInputStream(configEntry)); - log.info("configJson:{}", configJson); - } - - // 5.3 读取script.js脚本 - String scriptFile = "classes/script.js"; - JarEntry scriptEntity = jarFile.getJarEntry(scriptFile); - if (scriptEntity != null) { - // 5.4 读取脚本文件 - script = IoUtil.readUtf8(jarFile.getInputStream(scriptEntity)); - log.info("script:{}", script); - } - } catch (Exception e) { - throw exception(PLUGIN_INSTALL_FAILED); - } - - pluginInfoDo.setPluginId(pluginIdNew); - pluginInfoDo.setStatus(IotPluginStatusEnum.STOPPED.getStatus()); - pluginInfoDo.setFile(file.getOriginalFilename()); - pluginInfoDo.setConfigSchema(configJson); - pluginInfoDo.setScript(script); - - PluginDescriptor pluginDescriptor = pluginWrapper.getDescriptor(); - pluginInfoDo.setVersion(pluginDescriptor.getVersion()); - pluginInfoDo.setDescription(pluginDescriptor.getPluginDescription()); - pluginInfoMapper.updateById(pluginInfoDo); - } - - @Override - public void updatePluginStatus(Long id, Integer status) { - // 1. 校验存在 - PluginInfoDO pluginInfoDo = validatePluginInfoExists(id); - - // 插件状态无效 - if (!IotPluginStatusEnum.contains(status)) { - throw exception(PLUGIN_STATUS_INVALID); - } - - String pluginId = pluginInfoDo.getPluginId(); - PluginWrapper plugin = pluginManager.getPlugin(pluginId); - if (plugin != null) { - if (status.equals(IotPluginStatusEnum.RUNNING.getStatus()) && plugin.getPluginState() != PluginState.STARTED) { - // 启动插件 - pluginManager.startPlugin(pluginId); - } else if (status.equals(IotPluginStatusEnum.STOPPED.getStatus()) && plugin.getPluginState() == PluginState.STARTED) { - // 停止插件 - pluginManager.stopPlugin(pluginId); - } - } else { - // 已经停止,未获取到插件 - if (IotPluginStatusEnum.STOPPED.getStatus().equals(pluginInfoDo.getStatus())) { - throw exception(PLUGIN_STATUS_INVALID); - } - } - pluginInfoDo.setStatus(status); - pluginInfoMapper.updateById(pluginInfoDo); - } - -// @PostConstruct -// public void init() { -// Executors.newSingleThreadScheduledExecutor().schedule(this::startPlugins, 3, TimeUnit.SECONDS); -// } -// -// @SneakyThrows -// private void startPlugins() { -// for (PluginInfoDO pluginInfoDO : pluginInfoMapper.selectList()) { -// if (!IotPluginStatusEnum.RUNNING.getStatus().equals(pluginInfoDO.getStatus())) { -// continue; -// } -// log.info("start plugin:{}", pluginInfoDO.getPluginId()); -// try { -// pluginManager.startPlugin(pluginInfoDO.getPluginId()); -// } catch (Exception e) { -// log.error("start plugin error", e); -// } -// } -// } - -} \ No newline at end of file diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/plugininstance/PluginInstanceService.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/plugininstance/PluginInstanceService.java deleted file mode 100644 index 0789c4638..000000000 --- a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/plugininstance/PluginInstanceService.java +++ /dev/null @@ -1,54 +0,0 @@ -package cn.iocoder.yudao.module.iot.service.plugininstance; - -import cn.iocoder.yudao.framework.common.pojo.PageResult; -import cn.iocoder.yudao.module.iot.controller.admin.plugininstance.vo.PluginInstancePageReqVO; -import cn.iocoder.yudao.module.iot.controller.admin.plugininstance.vo.PluginInstanceSaveReqVO; -import cn.iocoder.yudao.module.iot.dal.dataobject.plugininstance.PluginInstanceDO; -import jakarta.validation.Valid; - -/** - * IoT 插件实例 Service 接口 - * - * @author 芋道源码 - */ -public interface PluginInstanceService { - - /** - * 创建IoT 插件实例 - * - * @param createReqVO 创建信息 - * @return 编号 - */ - Long createPluginInstance(@Valid PluginInstanceSaveReqVO createReqVO); - - /** - * 更新IoT 插件实例 - * - * @param updateReqVO 更新信息 - */ - void updatePluginInstance(@Valid PluginInstanceSaveReqVO updateReqVO); - - /** - * 删除IoT 插件实例 - * - * @param id 编号 - */ - void deletePluginInstance(Long id); - - /** - * 获得IoT 插件实例 - * - * @param id 编号 - * @return IoT 插件实例 - */ - PluginInstanceDO getPluginInstance(Long id); - - /** - * 获得IoT 插件实例分页 - * - * @param pageReqVO 分页查询 - * @return IoT 插件实例分页 - */ - PageResult getPluginInstancePage(PluginInstancePageReqVO pageReqVO); - -} \ No newline at end of file diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/plugininstance/PluginInstanceServiceImpl.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/plugininstance/PluginInstanceServiceImpl.java deleted file mode 100644 index 405efe163..000000000 --- a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/plugininstance/PluginInstanceServiceImpl.java +++ /dev/null @@ -1,70 +0,0 @@ -package cn.iocoder.yudao.module.iot.service.plugininstance; - -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.plugininstance.vo.PluginInstancePageReqVO; -import cn.iocoder.yudao.module.iot.controller.admin.plugininstance.vo.PluginInstanceSaveReqVO; -import cn.iocoder.yudao.module.iot.dal.dataobject.plugininstance.PluginInstanceDO; -import cn.iocoder.yudao.module.iot.dal.mysql.plugininstance.PluginInstanceMapper; -import jakarta.annotation.Resource; -import org.springframework.stereotype.Service; -import org.springframework.validation.annotation.Validated; - -import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception; -import static cn.iocoder.yudao.module.iot.enums.ErrorCodeConstants.PLUGIN_INSTANCE_NOT_EXISTS; - -/** - * IoT 插件实例 Service 实现类 - * - * @author 芋道源码 - */ -@Service -@Validated -public class PluginInstanceServiceImpl implements PluginInstanceService { - - @Resource - private PluginInstanceMapper pluginInstanceMapper; - - @Override - public Long createPluginInstance(PluginInstanceSaveReqVO createReqVO) { - // 插入 - PluginInstanceDO pluginInstance = BeanUtils.toBean(createReqVO, PluginInstanceDO.class); - pluginInstanceMapper.insert(pluginInstance); - // 返回 - return pluginInstance.getId(); - } - - @Override - public void updatePluginInstance(PluginInstanceSaveReqVO updateReqVO) { - // 校验存在 - validatePluginInstanceExists(updateReqVO.getId()); - // 更新 - PluginInstanceDO updateObj = BeanUtils.toBean(updateReqVO, PluginInstanceDO.class); - pluginInstanceMapper.updateById(updateObj); - } - - @Override - public void deletePluginInstance(Long id) { - // 校验存在 - validatePluginInstanceExists(id); - // 删除 - pluginInstanceMapper.deleteById(id); - } - - private void validatePluginInstanceExists(Long id) { - if (pluginInstanceMapper.selectById(id) == null) { - throw exception(PLUGIN_INSTANCE_NOT_EXISTS); - } - } - - @Override - public PluginInstanceDO getPluginInstance(Long id) { - return pluginInstanceMapper.selectById(id); - } - - @Override - public PageResult getPluginInstancePage(PluginInstancePageReqVO pageReqVO) { - return pluginInstanceMapper.selectPage(pageReqVO); - } - -} \ No newline at end of file diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/product/IotProductServiceImpl.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/product/IotProductServiceImpl.java index a53aee55d..d384e44c0 100644 --- a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/product/IotProductServiceImpl.java +++ b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/product/IotProductServiceImpl.java @@ -2,13 +2,14 @@ package cn.iocoder.yudao.module.iot.service.product; import cn.iocoder.yudao.framework.common.pojo.PageResult; import cn.iocoder.yudao.framework.common.util.object.BeanUtils; +import cn.iocoder.yudao.framework.tenant.core.util.TenantUtils; import cn.iocoder.yudao.module.iot.controller.admin.product.vo.product.IotProductPageReqVO; import cn.iocoder.yudao.module.iot.controller.admin.product.vo.product.IotProductSaveReqVO; import cn.iocoder.yudao.module.iot.dal.dataobject.product.IotProductDO; import cn.iocoder.yudao.module.iot.dal.mysql.product.IotProductMapper; import cn.iocoder.yudao.module.iot.enums.product.IotProductStatusEnum; +import cn.iocoder.yudao.module.iot.service.device.IotDevicePropertyDataService; import cn.iocoder.yudao.module.iot.service.tdengine.IotThingModelMessageService; -import cn.iocoder.yudao.module.iot.service.thingmodel.IotProductThingModelService; import com.baomidou.dynamic.datasource.annotation.DSTransactional; import jakarta.annotation.Resource; import org.springframework.context.annotation.Lazy; @@ -33,19 +34,23 @@ public class IotProductServiceImpl implements IotProductService { @Resource private IotProductMapper productMapper; - @Resource - @Lazy // 延迟加载,解决循环依赖 - private IotProductThingModelService thingModelFunctionService; @Resource @Lazy // 延迟加载,解决循环依赖 private IotThingModelMessageService thingModelMessageService; + @Resource + @Lazy // 延迟加载,解决循环依赖 + private IotDevicePropertyDataService devicePropertyDataService; @Override public Long createProduct(IotProductSaveReqVO createReqVO) { - // 1. 生成 ProductKey - if (productMapper.selectByProductKey(createReqVO.getProductKey()) != null) { - throw exception(PRODUCT_KEY_EXISTS); - } + // 1. 校验 ProductKey + TenantUtils.executeIgnore(() -> { + // 为什么忽略租户?避免多个租户之间,productKey 重复,导致 TDengine 设备属性表重复 + if (productMapper.selectByProductKey(createReqVO.getProductKey()) != null) { + throw exception(PRODUCT_KEY_EXISTS); + } + }); + // 2. 插入 IotProductDO product = BeanUtils.toBean(createReqVO, IotProductDO.class) .setStatus(IotProductStatusEnum.UNPUBLISHED.getStatus()); @@ -119,8 +124,8 @@ public class IotProductServiceImpl implements IotProductService { // 3. 产品是发布状态 if (Objects.equals(status, IotProductStatusEnum.PUBLISHED.getStatus())) { // 3.1 创建产品超级表数据模型 - thingModelFunctionService.createSuperTableDataModel(id); - // 3.2 创建物模型日志超级表数据模型 + devicePropertyDataService.defineDevicePropertyData(id); + // 3.2 创建物模型日志超级表数据模型 TODO 待定:message 要不要分; thingModelMessageService.createSuperTable(id); } productMapper.updateById(updateObj); diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/tdengine/IotSuperTableService.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/tdengine/IotSuperTableService.java deleted file mode 100644 index bb6d8c077..000000000 --- a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/tdengine/IotSuperTableService.java +++ /dev/null @@ -1,19 +0,0 @@ -package cn.iocoder.yudao.module.iot.service.tdengine; - - -import cn.iocoder.yudao.module.iot.dal.dataobject.product.IotProductDO; -import cn.iocoder.yudao.module.iot.dal.dataobject.thingmodel.IotProductThingModelDO; - -import java.util.List; - -/** - * IoT 超级表服务,负责根据物模型创建和更新超级表,以及创建超级表的子表等操作。 - */ -public interface IotSuperTableService { - - /** - * 创建超级表数据模型 - */ - void createSuperTableDataModel(IotProductDO product, List thingModelList); - -} \ No newline at end of file diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/tdengine/IotSuperTableServiceImpl.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/tdengine/IotSuperTableServiceImpl.java deleted file mode 100644 index 1727fae62..000000000 --- a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/tdengine/IotSuperTableServiceImpl.java +++ /dev/null @@ -1,260 +0,0 @@ -package cn.iocoder.yudao.module.iot.service.tdengine; - -import cn.hutool.core.bean.BeanUtil; -import cn.hutool.core.collection.CollUtil; -import cn.iocoder.yudao.module.iot.controller.admin.thingmodel.model.ThingModelProperty; -import cn.iocoder.yudao.module.iot.controller.admin.thingmodel.model.ThingModelRespVO; -import cn.iocoder.yudao.module.iot.dal.dataobject.product.IotProductDO; -import cn.iocoder.yudao.module.iot.dal.dataobject.tdengine.FieldParser; -import cn.iocoder.yudao.module.iot.dal.dataobject.tdengine.TdFieldDO; -import cn.iocoder.yudao.module.iot.dal.dataobject.tdengine.TdTableDO; -import cn.iocoder.yudao.module.iot.dal.dataobject.thingmodel.IotProductThingModelDO; -import cn.iocoder.yudao.module.iot.dal.tdengine.TdEngineDDLMapper; -import cn.iocoder.yudao.module.iot.enums.thingmodel.IotProductThingModelTypeEnum; -import jakarta.annotation.Resource; -import lombok.extern.slf4j.Slf4j; -import org.springframework.beans.factory.annotation.Value; -import org.springframework.stereotype.Service; - -import java.util.*; -import java.util.stream.Collectors; - -/** - * IoT 超级表服务实现类,负责根据物模型创建和更新超级表,以及创建超级表的子表等操作。 - */ -@Service -@Slf4j -public class IotSuperTableServiceImpl implements IotSuperTableService { - - @Resource - private TdEngineDDLMapper tdEngineDDLMapper; - - @Value("${spring.datasource.dynamic.datasource.tdengine.url}") - private String url; - - @Override - public void createSuperTableDataModel(IotProductDO product, List thingModelList) { - ThingModelRespVO thingModel = buildThingModel(product, thingModelList); - - if (thingModel.getModel() == null || CollUtil.isEmpty(thingModel.getModel().getProperties())) { - log.warn("物模型属性列表为空,不创建超级表"); - return; - } - - String superTableName = getSuperTableName(product.getDeviceType(), product.getProductKey()); - String databaseName = getDatabaseName(); - - List> results = tdEngineDDLMapper.showSuperTables(new TdTableDO(databaseName, superTableName)); - int tableExists = results == null || results.isEmpty() ? 0 : results.size(); - if (tableExists > 0) { - updateSuperTable(thingModel, product.getDeviceType()); - } else { - createSuperTable(thingModel, product.getDeviceType()); - } - } - - /** - * 创建超级表 - */ - private void createSuperTable(ThingModelRespVO thingModel, Integer deviceType) { - // 解析物模型,获取字段列表 - List schemaFields = new ArrayList<>(); - schemaFields.add(TdFieldDO.builder() - .fieldName("time") - .dataType("TIMESTAMP") - .build()); - schemaFields.addAll(FieldParser.parse(thingModel)); - - // 设置超级表的标签 - List tagsFields = List.of( - TdFieldDO.builder().fieldName("product_key").dataType("NCHAR").dataLength(64).build(), - TdFieldDO.builder().fieldName("device_key").dataType("NCHAR").dataLength(64).build(), - TdFieldDO.builder().fieldName("device_name").dataType("NCHAR").dataLength(64).build(), - TdFieldDO.builder().fieldName("device_type").dataType("INT").build() - ); - - // 获取超级表的名称和数据库名称 - String superTableName = getSuperTableName(deviceType, thingModel.getProductKey()); - String databaseName = getDatabaseName(); - - // 创建超级表 - tdEngineDDLMapper.createSuperTable(new TdTableDO(databaseName, superTableName, schemaFields, tagsFields)); - } - - /** - * 更新超级表 - */ - private void updateSuperTable(ThingModelRespVO thingModel, Integer deviceType) { - String superTableName = getSuperTableName(deviceType, thingModel.getProductKey()); - try { - List oldFields = getTableFields(superTableName); - List newFields = FieldParser.parse(thingModel); - - updateTableFields(superTableName, oldFields, newFields); - } catch (Exception e) { - log.error("更新物模型超级表失败: {}", e.getMessage(), e); - } - } - - /** - * 获取表的字段信息 - */ - private List getTableFields(String tableName) { - List> tableDescription = tdEngineDDLMapper.describeSuperTable(new TdTableDO(getDatabaseName(), tableName)); - if (CollUtil.isEmpty(tableDescription)) { - return Collections.emptyList(); - } - - return tableDescription.stream() - .filter(map -> !"TAG".equals(map.get("note"))) - .filter(map -> !"time".equals(map.get("field"))) - .map(map -> TdFieldDO.builder() - .fieldName((String) map.get("field")) - .dataType((String) map.get("type")) - .dataLength((Integer) map.get("length")) - .build()) - .collect(Collectors.toList()); - } - - /** - * 更新表的字段,包括新增、修改和删除字段 - */ - private void updateTableFields(String tableName, List oldFields, List newFields) { - String databaseName = getDatabaseName(); - - // 获取新增、修改、删除的字段 - List addFields = getAddFields(oldFields, newFields); - List modifyFields = getModifyFields(oldFields, newFields); - List dropFields = getDropFields(oldFields, newFields); - - // 添加新增字段 - if (CollUtil.isNotEmpty(addFields)) { - for (TdFieldDO addField : addFields) { - tdEngineDDLMapper.addColumnForSuperTable(TdTableDO.builder() - .dataBaseName(databaseName) - .superTableName(tableName) - .column(addField) - .build()); - } - } - // 删除旧字段 - if (CollUtil.isNotEmpty(dropFields)) { - for (TdFieldDO dropField : dropFields) { - tdEngineDDLMapper.dropColumnForSuperTable(TdTableDO.builder() - .dataBaseName(databaseName) - .superTableName(tableName) - .column(dropField) - .build()); - } - } - // 修改字段(先删除再添加) - if (CollUtil.isNotEmpty(modifyFields)) { - for (TdFieldDO modifyField : modifyFields) { - tdEngineDDLMapper.dropColumnForSuperTable(TdTableDO.builder() - .dataBaseName(databaseName) - .superTableName(tableName) - .column(modifyField) - .build()); - tdEngineDDLMapper.addColumnForSuperTable(TdTableDO.builder() - .dataBaseName(databaseName) - .superTableName(tableName) - .column(modifyField) - .build()); - } - } - } - - /** - * 获取需要新增的字段 - */ - private List getAddFields(List oldFields, List newFields) { - Set oldFieldNames = oldFields.stream() - .map(TdFieldDO::getFieldName) - .collect(Collectors.toSet()); - return newFields.stream() - .filter(f -> !oldFieldNames.contains(f.getFieldName())) - .collect(Collectors.toList()); - } - - /** - * 获取需要修改的字段 - */ - private List getModifyFields(List oldFields, List newFields) { - Map oldFieldMap = oldFields.stream() - .collect(Collectors.toMap(TdFieldDO::getFieldName, f -> f)); - - return newFields.stream() - .filter(f -> { - TdFieldDO oldField = oldFieldMap.get(f.getFieldName()); - return oldField != null && ( - !oldField.getDataType().equals(f.getDataType()) || - !Objects.equals(oldField.getDataLength(), f.getDataLength()) - ); - }) - .collect(Collectors.toList()); - } - - /** - * 获取需要删除的字段 - */ - private List getDropFields(List oldFields, List newFields) { - Set newFieldNames = newFields.stream() - .map(TdFieldDO::getFieldName) - .collect(Collectors.toSet()); - return oldFields.stream() - .filter(f -> !"time".equals(f.getFieldName())) - .filter(f -> !newFieldNames.contains(f.getFieldName())) - .collect(Collectors.toList()); - } - - /** - * 构建物模型 - */ - private ThingModelRespVO buildThingModel(IotProductDO product, List thingModelList) { - ThingModelRespVO thingModel = new ThingModelRespVO(); - thingModel.setId(product.getId()); - thingModel.setProductKey(product.getProductKey()); - - List properties = thingModelList.stream() - .filter(item -> IotProductThingModelTypeEnum.PROPERTY.equals( - IotProductThingModelTypeEnum.valueOfType(item.getType()))) - .map(this::buildThingModelProperty) - .collect(Collectors.toList()); - - ThingModelRespVO.Model model = new ThingModelRespVO.Model(); - model.setProperties(properties); - thingModel.setModel(model); - - return thingModel; - } - - /** - * 构建物模型属性 - */ - private ThingModelProperty buildThingModelProperty(IotProductThingModelDO thingModel) { - ThingModelProperty property = BeanUtil.copyProperties(thingModel, ThingModelProperty.class); - property.setDataType(thingModel.getProperty().getDataType()); - return property; - } - - /** - * 获取数据库名称 - */ - private String getDatabaseName() { - int index = url.lastIndexOf("/"); - return index != -1 ? url.substring(index + 1) : url; - } - - /** - * 获取超级表名称 - */ - private String getSuperTableName(Integer deviceType, String productKey) { - String prefix = switch (deviceType) { - case 1 -> "gateway_sub_"; - case 2 -> "gateway_"; - default -> "device_"; - }; - return (prefix + productKey).toLowerCase(); - } - -} \ No newline at end of file diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/tdengine/IotThingModelMessageServiceImpl.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/tdengine/IotThingModelMessageServiceImpl.java index 87bf01d26..d91041995 100644 --- a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/tdengine/IotThingModelMessageServiceImpl.java +++ b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/tdengine/IotThingModelMessageServiceImpl.java @@ -11,16 +11,16 @@ import cn.iocoder.yudao.module.iot.dal.dataobject.tdengine.FieldParser; import cn.iocoder.yudao.module.iot.dal.dataobject.tdengine.TdFieldDO; import cn.iocoder.yudao.module.iot.dal.dataobject.tdengine.TdTableDO; import cn.iocoder.yudao.module.iot.dal.dataobject.tdengine.ThingModelMessage; -import cn.iocoder.yudao.module.iot.dal.dataobject.thingmodel.IotProductThingModelDO; +import cn.iocoder.yudao.module.iot.dal.dataobject.thingmodel.IotThingModelDO; import cn.iocoder.yudao.module.iot.dal.dataobject.product.IotProductDO; import cn.iocoder.yudao.module.iot.dal.redis.deviceData.DeviceDataRedisDAO; import cn.iocoder.yudao.module.iot.dal.tdengine.TdEngineDDLMapper; import cn.iocoder.yudao.module.iot.dal.tdengine.TdEngineDMLMapper; import cn.iocoder.yudao.module.iot.enums.IotConstants; import cn.iocoder.yudao.module.iot.enums.device.IotDeviceStatusEnum; -import cn.iocoder.yudao.module.iot.enums.thingmodel.IotProductThingModelTypeEnum; +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.thingmodel.IotProductThingModelService; +import cn.iocoder.yudao.module.iot.service.thingmodel.IotThingModelService; import cn.iocoder.yudao.module.iot.service.product.IotProductService; import cn.iocoder.yudao.module.iot.util.IotTdDatabaseUtils; import jakarta.annotation.Resource; @@ -52,7 +52,7 @@ public class IotThingModelMessageServiceImpl implements IotThingModelMessageServ private String url; @Resource - private IotProductThingModelService iotProductThingModelService; + private IotThingModelService iotThingModelService; @Resource private IotDeviceService iotDeviceService; @Resource @@ -83,7 +83,7 @@ public class IotThingModelMessageServiceImpl implements IotThingModelMessageServ // 2. 获取设备属性并进行物模型校验,过滤非物模型属性 Map params = thingModelMessage.dataToMap(); - List thingModelList = getValidThingModelList(thingModelMessage.getProductKey()); + List thingModelList = getValidThingModelList(thingModelMessage.getProductKey()); if (thingModelList.isEmpty()) { return; } @@ -102,9 +102,9 @@ public class IotThingModelMessageServiceImpl implements IotThingModelMessageServ .build()); } - private List getValidThingModelList(String productKey) { - return filterList(iotProductThingModelService.getProductThingModelListByProductKey(productKey), - thingModel -> IotProductThingModelTypeEnum.PROPERTY.getType().equals(thingModel.getType())); + private List getValidThingModelList(String productKey) { + return filterList(iotThingModelService.getProductThingModelListByProductKey(productKey), + thingModel -> IotThingModelTypeEnum.PROPERTY.getType().equals(thingModel.getType())); } @Override @@ -134,21 +134,17 @@ public class IotThingModelMessageServiceImpl implements IotThingModelMessageServ tdEngineDDLMapper.createSuperTable(new TdTableDO(databaseName, superTableName, schemaFields, tagsFields)); } - private List getValidFunctionList(String productKey) { - // TODO @puhui999:使用 convertList 会好点哈 - return iotProductThingModelService - .getProductThingModelListByProductKey(productKey) - .stream() - .filter(function -> IotProductThingModelTypeEnum.PROPERTY.getType().equals(function.getType())) - .toList(); + private List getValidFunctionList(String productKey) { + return filterList(iotThingModelService.getProductThingModelListByProductKey(productKey), + thingModel -> IotThingModelTypeEnum.PROPERTY.getType().equals(thingModel.getType())); } - private List filterAndCollectValidFields(Map params, List thingModelList, IotDeviceDO device, Long time) { + private List filterAndCollectValidFields(Map params, List thingModelList, IotDeviceDO device, Long time) { // 1. 获取属性标识符集合 - Set propertyIdentifiers = convertSet(thingModelList, IotProductThingModelDO::getIdentifier); + Set propertyIdentifiers = convertSet(thingModelList, IotThingModelDO::getIdentifier); // 2. 构建属性标识符和属性的映射 - Map thingModelMap = convertMap(thingModelList, IotProductThingModelDO::getIdentifier); + Map thingModelMap = convertMap(thingModelList, IotThingModelDO::getIdentifier); // 3. 过滤并收集有效的属性字段 List schemaFieldValues = new ArrayList<>(); @@ -168,21 +164,21 @@ public class IotThingModelMessageServiceImpl implements IotThingModelMessageServ * 缓存设备属性 * * @param device 设备信息 - * @param iotProductThingModelDO 物模型属性 + * @param iotThingModelDO 物模型属性 * @param val 属性值 * @param time 时间 */ - private void setDeviceDataCache(IotDeviceDO device, IotProductThingModelDO iotProductThingModelDO, Object val, Long time) { + private void setDeviceDataCache(IotDeviceDO device, IotThingModelDO iotThingModelDO, Object val, Long time) { IotDeviceDataDO deviceData = IotDeviceDataDO.builder() .productKey(device.getProductKey()) .deviceName(device.getDeviceName()) - .identifier(iotProductThingModelDO.getIdentifier()) + .identifier(iotThingModelDO.getIdentifier()) .value(val != null ? val.toString() : null) .updateTime(DateUtil.toLocalDateTime(new Date(time))) .deviceId(device.getId()) - .thingModelId(iotProductThingModelDO.getId()) - .name(iotProductThingModelDO.getName()) - .dataType(iotProductThingModelDO.getProperty().getDataType()) + .thingModelId(iotThingModelDO.getId()) + .name(iotThingModelDO.getName()) + .dataType(iotThingModelDO.getProperty().getDataType()) .build(); deviceDataRedisDAO.set(deviceData); } diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/thingmodel/IotProductThingModelServiceImpl.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/thingmodel/IotProductThingModelServiceImpl.java deleted file mode 100644 index b5c2d4eee..000000000 --- a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/thingmodel/IotProductThingModelServiceImpl.java +++ /dev/null @@ -1,442 +0,0 @@ -package cn.iocoder.yudao.module.iot.service.thingmodel; - -import cn.hutool.core.collection.CollUtil; -import cn.hutool.core.util.ObjectUtil; -import cn.iocoder.yudao.framework.common.pojo.PageResult; -import cn.iocoder.yudao.framework.common.util.collection.CollectionUtils; -import cn.iocoder.yudao.framework.common.util.object.ObjectUtils; -import cn.iocoder.yudao.module.iot.controller.admin.thingmodel.model.ThingModelEvent; -import cn.iocoder.yudao.module.iot.controller.admin.thingmodel.model.ThingModelProperty; -import cn.iocoder.yudao.module.iot.controller.admin.thingmodel.model.ThingModelService; -import cn.iocoder.yudao.module.iot.controller.admin.thingmodel.model.dataType.ThingModelArgument; -import cn.iocoder.yudao.module.iot.controller.admin.thingmodel.model.dataType.ThingModelArrayDataSpecs; -import cn.iocoder.yudao.module.iot.controller.admin.thingmodel.model.dataType.ThingModelDateOrTextDataSpecs; -import cn.iocoder.yudao.module.iot.controller.admin.thingmodel.vo.IotProductThingModelPageReqVO; -import cn.iocoder.yudao.module.iot.controller.admin.thingmodel.vo.IotProductThingModelSaveReqVO; -import cn.iocoder.yudao.module.iot.convert.thingmodel.IotProductThingModelConvert; -import cn.iocoder.yudao.module.iot.dal.dataobject.product.IotProductDO; -import cn.iocoder.yudao.module.iot.dal.dataobject.thingmodel.IotProductThingModelDO; -import cn.iocoder.yudao.module.iot.dal.mysql.thingmodel.IotProductThingModelMapper; -import cn.iocoder.yudao.module.iot.enums.product.IotProductStatusEnum; -import cn.iocoder.yudao.module.iot.enums.thingmodel.IotProductThingModelAccessModeEnum; -import cn.iocoder.yudao.module.iot.enums.thingmodel.IotProductThingModelTypeEnum; -import cn.iocoder.yudao.module.iot.service.product.IotProductService; -import cn.iocoder.yudao.module.iot.service.tdengine.IotSuperTableService; -import jakarta.annotation.Resource; -import lombok.extern.slf4j.Slf4j; -import org.springframework.stereotype.Service; -import org.springframework.transaction.annotation.Transactional; -import org.springframework.validation.annotation.Validated; - -import java.util.*; -import java.util.stream.Collectors; - -import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception; -import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.diffList; -import static cn.iocoder.yudao.module.iot.enums.ErrorCodeConstants.*; - -/** - * IoT 产品物模型 Service 实现类 - * - * @author 芋道源码 - */ -@Service -@Validated -@Slf4j -public class IotProductThingModelServiceImpl implements IotProductThingModelService { - - @Resource - private IotProductThingModelMapper productThingModelMapper; - - @Resource - private IotProductService productService; - @Resource - private IotSuperTableService dbStructureDataService; - - @Override - @Transactional(rollbackFor = Exception.class) - public Long createProductThingModel(IotProductThingModelSaveReqVO createReqVO) { - // 1. 校验功能标识符在同一产品下是否唯一 - validateIdentifierUnique(createReqVO.getProductId(), createReqVO.getIdentifier()); - - // 2. 功能名称在同一产品下是否唯一 - validateNameUnique(createReqVO.getProductId(), createReqVO.getName()); - - // 3. 系统保留字段,不能用于标识符定义 - validateNotDefaultEventAndService(createReqVO.getIdentifier()); - - // 4. 校验产品状态,发布状态下,不允许新增功能 - validateProductStatus(createReqVO.getProductId()); - - // 5. 插入数据库 - IotProductThingModelDO thingModel = IotProductThingModelConvert.INSTANCE.convert(createReqVO); - productThingModelMapper.insert(thingModel); - - // 6. 如果创建的是属性,需要更新默认的事件和服务 - if (Objects.equals(createReqVO.getType(), IotProductThingModelTypeEnum.PROPERTY.getType())) { - createDefaultEventsAndServices(createReqVO.getProductId(), createReqVO.getProductKey()); - } - return thingModel.getId(); - } - - private void validateProductStatus(Long createReqVO) { - IotProductDO product = productService.getProduct(createReqVO); - if (Objects.equals(product.getStatus(), IotProductStatusEnum.PUBLISHED.getStatus())) { - throw exception(PRODUCT_STATUS_NOT_ALLOW_THING_MODEL); - } - } - - private void validateNotDefaultEventAndService(String identifier) { - // set, get, post, property, event, time, value 是系统保留字段,不能用于标识符定义 - if (CollUtil.containsAny(Arrays.asList("set", "get", "post", "property", "event", "time", "value"), Collections.singletonList(identifier))) { - throw exception(THING_MODEL_IDENTIFIER_INVALID); - } - } - - private void validateNameUnique(Long productId, String name) { - IotProductThingModelDO thingModel = productThingModelMapper.selectByProductIdAndName(productId, name); - if (thingModel != null) { - throw exception(THING_MODEL_NAME_EXISTS); - } - } - - private void validateIdentifierUnique(Long productId, String identifier) { - IotProductThingModelDO thingModel = productThingModelMapper.selectByProductIdAndIdentifier(productId, identifier); - if (thingModel != null) { - throw exception(THING_MODEL_IDENTIFIER_EXISTS); - } - } - - @Override - @Transactional(rollbackFor = Exception.class) - public void updateProductThingModel(IotProductThingModelSaveReqVO updateReqVO) { - // 1. 校验功能是否存在 - validateProductThingModelMapperExists(updateReqVO.getId()); - - // 2. 校验功能标识符是否唯一 - validateIdentifierUniqueForUpdate(updateReqVO.getId(), updateReqVO.getProductId(), updateReqVO.getIdentifier()); - - // 3. 校验产品状态,发布状态下,不允许操作功能 - validateProductStatus(updateReqVO.getProductId()); - - // 4. 更新数据库 - IotProductThingModelDO thingModel = IotProductThingModelConvert.INSTANCE.convert(updateReqVO); - productThingModelMapper.updateById(thingModel); - - // 5. 如果更新的是属性,需要更新默认的事件和服务 - if (Objects.equals(updateReqVO.getType(), IotProductThingModelTypeEnum.PROPERTY.getType())) { - createDefaultEventsAndServices(updateReqVO.getProductId(), updateReqVO.getProductKey()); - } - } - - private void validateIdentifierUniqueForUpdate(Long id, Long productId, String identifier) { - IotProductThingModelDO thingModel = productThingModelMapper.selectByProductIdAndIdentifier(productId, identifier); - if (thingModel != null && ObjectUtil.notEqual(thingModel.getId(), id)) { - throw exception(THING_MODEL_IDENTIFIER_EXISTS); - } - } - - @Override - @Transactional(rollbackFor = Exception.class) - public void deleteProductThingModel(Long id) { - // 1. 校验功能是否存在 - IotProductThingModelDO thingModel = productThingModelMapper.selectById(id); - if (thingModel == null) { - throw exception(THING_MODEL_NOT_EXISTS); - } - - // 3. 校验产品状态,发布状态下,不允许操作功能 - validateProductStatus(thingModel.getProductId()); - - // 2. 删除功能 - productThingModelMapper.deleteById(id); - - // 3. 如果删除的是属性,需要更新默认的事件和服务 - if (Objects.equals(thingModel.getType(), IotProductThingModelTypeEnum.PROPERTY.getType())) { - createDefaultEventsAndServices(thingModel.getProductId(), thingModel.getProductKey()); - } - } - - /** - * 校验功能是否存在 - * - * @param id 功能编号 - */ - private void validateProductThingModelMapperExists(Long id) { - if (productThingModelMapper.selectById(id) == null) { - throw exception(THING_MODEL_NOT_EXISTS); - } - } - - @Override - public IotProductThingModelDO getProductThingModel(Long id) { - return productThingModelMapper.selectById(id); - } - - @Override - public List getProductThingModelListByProductId(Long productId) { - return productThingModelMapper.selectListByProductId(productId); - } - - @Override - public PageResult getProductThingModelPage(IotProductThingModelPageReqVO pageReqVO) { - return productThingModelMapper.selectPage(pageReqVO); - } - - @Override - public void createSuperTableDataModel(Long productId) { - // 1. 查询产品 - IotProductDO product = productService.getProduct(productId); - - // 2. 查询产品的物模型功能列表 - List thingModelList = productThingModelMapper.selectListByProductId(productId); - - // 3. 生成 TDengine 的数据模型 - dbStructureDataService.createSuperTableDataModel(product, thingModelList); - } - - @Override - public List getProductThingModelListByProductKey(String productKey) { - return productThingModelMapper.selectListByProductKey(productKey); - } - - /** - * 创建默认的事件和服务 - */ - public void createDefaultEventsAndServices(Long productId, String productKey) { - // 1. 获取当前属性列表 - List propertyList = productThingModelMapper - .selectListByProductIdAndType(productId, IotProductThingModelTypeEnum.PROPERTY.getType()); - - // 2. 生成新的事件和服务列表 - List newThingModelList = new ArrayList<>(); - // 生成属性上报事件 - ThingModelEvent propertyPostEvent = generatePropertyPostEvent(propertyList); - if (propertyPostEvent != null) { - IotProductThingModelDO eventThingModel = buildEventThingModelDO(productId, productKey, propertyPostEvent); - newThingModelList.add(eventThingModel); - } - // 生成属性设置服务 - ThingModelService propertySetService = generatePropertySetService(propertyList); - if (propertySetService != null) { - IotProductThingModelDO setServiceThingModel = buildServiceThingModelDO(productId, productKey, propertySetService); - newThingModelList.add(setServiceThingModel); - } - // 生成属性获取服务 - ThingModelService propertyGetService = generatePropertyGetService(propertyList); - if (propertyGetService != null) { - IotProductThingModelDO getServiceThingModel = buildServiceThingModelDO(productId, productKey, propertyGetService); - newThingModelList.add(getServiceThingModel); - } - - // 3. 获取数据库中的默认的旧事件和服务列表 - List oldThingModelList = productThingModelMapper.selectListByProductIdAndIdentifiersAndTypes( - productId, - Arrays.asList("post", "set", "get"), - Arrays.asList(IotProductThingModelTypeEnum.EVENT.getType(), IotProductThingModelTypeEnum.SERVICE.getType()) - ); - - // 3.1 使用 diffList 方法比较新旧列表 - List> diffResult = diffList(oldThingModelList, newThingModelList, - // 继续使用 identifier 和 type 进行比较:这样可以准确地匹配对应的功能对象。 - (oldFunc, newFunc) -> Objects.equals(oldFunc.getIdentifier(), newFunc.getIdentifier()) - && Objects.equals(oldFunc.getType(), newFunc.getType())); - List createList = diffResult.get(0); // 需要新增的 - List updateList = diffResult.get(1); // 需要更新的 - List deleteList = diffResult.get(2); // 需要删除的 - - // 3.2 批量执行数据库操作 - // 新增数据库中的新事件和服务列表 - if (CollUtil.isNotEmpty(createList)) { - productThingModelMapper.insertBatch(createList); - } - // 更新数据库中的事件和服务列表 - if (CollUtil.isNotEmpty(updateList)) { - // 首先,为每个需要更新的对象设置其对应的 ID - updateList.forEach(updateFunc -> { - IotProductThingModelDO oldFunc = findThingModelByIdentifierAndType( - oldThingModelList, updateFunc.getIdentifier(), updateFunc.getType()); - if (oldFunc != null) { - updateFunc.setId(oldFunc.getId()); - } - }); - // 过滤掉没有设置 ID 的对象 - List validUpdateList = updateList.stream() - .filter(func -> func.getId() != null) - .collect(Collectors.toList()); - // 执行批量更新 - if (CollUtil.isNotEmpty(validUpdateList)) { - productThingModelMapper.updateBatch(validUpdateList); - } - } - - // 删除数据库中的旧事件和服务列表 - if (CollUtil.isNotEmpty(deleteList)) { - Set idsToDelete = CollectionUtils.convertSet(deleteList, IotProductThingModelDO::getId); - productThingModelMapper.deleteByIds(idsToDelete); - } - } - - /** - * 根据标识符和类型查找功能对象 - */ - private IotProductThingModelDO findThingModelByIdentifierAndType(List thingModelList, - String identifier, Integer type) { - return CollUtil.findOne(thingModelList, func -> - Objects.equals(func.getIdentifier(), identifier) && Objects.equals(func.getType(), type)); - } - - /** - * 构建事件功能对象 - */ - private IotProductThingModelDO buildEventThingModelDO(Long productId, String productKey, ThingModelEvent event) { - return new IotProductThingModelDO() - .setProductId(productId) - .setProductKey(productKey) - .setIdentifier(event.getIdentifier()) - .setName(event.getName()) - .setDescription(event.getDescription()) - .setType(IotProductThingModelTypeEnum.EVENT.getType()) - .setEvent(event); - } - - /** - * 构建服务功能对象 - */ - private IotProductThingModelDO buildServiceThingModelDO(Long productId, String productKey, ThingModelService service) { - return new IotProductThingModelDO() - .setProductId(productId) - .setProductKey(productKey) - .setIdentifier(service.getIdentifier()) - .setName(service.getName()) - .setDescription(service.getDescription()) - .setType(IotProductThingModelTypeEnum.SERVICE.getType()) - .setService(service); - } - - /** - * 生成属性上报事件 - */ - private ThingModelEvent generatePropertyPostEvent(List propertyList) { - if (CollUtil.isEmpty(propertyList)) { - return null; - } - - ThingModelEvent event = new ThingModelEvent() - .setIdentifier("post") - .setName("属性上报") - .setType("info") - .setDescription("属性上报事件") - .setMethod("thing.event.property.post"); - - // 将属性列表转换为事件的输出参数 - List outputData = new ArrayList<>(); - for (IotProductThingModelDO thingModel : propertyList) { - ThingModelArgument arg = new ThingModelArgument() - .setIdentifier(thingModel.getIdentifier()) - .setName(thingModel.getName()) - .setProperty(thingModel.getProperty()) - .setDescription(thingModel.getDescription()) - .setDirection("output"); // 设置为输出参数 - outputData.add(arg); - } - event.setOutputData(outputData); - return event; - } - - /** - * 生成属性设置服务 - */ - private ThingModelService generatePropertySetService(List propertyList) { - if (propertyList == null || propertyList.isEmpty()) { - return null; - } - - List inputData = new ArrayList<>(); - for (IotProductThingModelDO thingModel : propertyList) { - ThingModelProperty property = thingModel.getProperty(); - if (IotProductThingModelAccessModeEnum.READ_WRITE.getMode().equals(property.getAccessMode())) { - ThingModelArgument arg = new ThingModelArgument() - .setIdentifier(property.getIdentifier()) - .setName(property.getName()) - .setProperty(property) - .setDescription(property.getDescription()) - .setDirection("input"); // 设置为输入参数 - inputData.add(arg); - } - } - if (inputData.isEmpty()) { - // 如果没有可写属性,不生成属性设置服务 - return null; - } - - // 属性设置服务一般不需要输出参数 - return new ThingModelService() - .setIdentifier("set") - .setName("属性设置") - .setCallType("async") - .setDescription("属性设置服务") - .setMethod("thing.service.property.set") - .setInputData(inputData) - // 属性设置服务一般不需要输出参数 - .setOutputData(new ArrayList<>()); - } - - /** - * 生成属性获取服务 - */ - private ThingModelService generatePropertyGetService(List propertyList) { - if (propertyList == null || propertyList.isEmpty()) { - return null; - } - - List outputData = new ArrayList<>(); - for (IotProductThingModelDO thingModelDO : propertyList) { - ThingModelProperty property = thingModelDO.getProperty(); - if (ObjectUtils.equalsAny(property.getAccessMode(), - IotProductThingModelAccessModeEnum.READ_ONLY.getMode(), IotProductThingModelAccessModeEnum.READ_WRITE.getMode())) { - ThingModelArgument arg = new ThingModelArgument() - .setIdentifier(property.getIdentifier()) - .setName(property.getName()) - .setProperty(property) - .setDescription(property.getDescription()) - .setDirection("output"); // 设置为输出参数 - outputData.add(arg); - } - } - if (outputData.isEmpty()) { - // 如果没有可读属性,不生成属性获取服务 - return null; - } - - ThingModelService service = new ThingModelService() - .setIdentifier("get") - .setName("属性获取") - .setCallType("async") - .setDescription("属性获取服务") - .setMethod("thing.service.property.get"); - - // 定义输入参数:属性标识符列表 - ThingModelArgument inputArg = new ThingModelArgument() - .setIdentifier("properties") - .setName("属性标识符列表") - .setDescription("需要获取的属性标识符列表") - .setDirection("input"); // 设置为输入参数 - - // 创建数组类型,元素类型为文本类型(字符串)TODO @puhui999: 还得研究研究 - ThingModelArrayDataSpecs arrayType = new ThingModelArrayDataSpecs(); - arrayType.setDataType("array"); - inputArg.setProperty(new ThingModelProperty().setIdentifier(inputArg.getIdentifier()).setName(inputArg.getName()) - .setDescription(inputArg.getDescription()).setDataSpecs(arrayType)); - - ThingModelDateOrTextDataSpecs textType = new ThingModelDateOrTextDataSpecs(); - textType.setDataType("text"); - inputArg.setProperty(new ThingModelProperty().setIdentifier(inputArg.getIdentifier()).setName(inputArg.getName()) - .setDescription(inputArg.getDescription()).setDataSpecs(textType)); - - service.setInputData(Collections.singletonList(inputArg)); - service.setOutputData(outputData); - return service; - } - -} diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/thingmodel/IotProductThingModelService.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/thingmodel/IotThingModelService.java similarity index 59% rename from yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/thingmodel/IotProductThingModelService.java rename to yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/thingmodel/IotThingModelService.java index 04086d0a7..f2ec53ecf 100644 --- a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/thingmodel/IotProductThingModelService.java +++ b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/thingmodel/IotThingModelService.java @@ -1,9 +1,9 @@ package cn.iocoder.yudao.module.iot.service.thingmodel; import cn.iocoder.yudao.framework.common.pojo.PageResult; -import cn.iocoder.yudao.module.iot.controller.admin.thingmodel.vo.IotProductThingModelPageReqVO; -import cn.iocoder.yudao.module.iot.controller.admin.thingmodel.vo.IotProductThingModelSaveReqVO; -import cn.iocoder.yudao.module.iot.dal.dataobject.thingmodel.IotProductThingModelDO; +import cn.iocoder.yudao.module.iot.controller.admin.thingmodel.vo.IotThingModelPageReqVO; +import cn.iocoder.yudao.module.iot.controller.admin.thingmodel.vo.IotThingModelSaveReqVO; +import cn.iocoder.yudao.module.iot.dal.dataobject.thingmodel.IotThingModelDO; import jakarta.validation.Valid; import java.util.List; @@ -13,7 +13,7 @@ import java.util.List; * * @author 芋道源码 */ -public interface IotProductThingModelService { +public interface IotThingModelService { /** * 创建产品物模型 @@ -21,21 +21,21 @@ public interface IotProductThingModelService { * @param createReqVO 创建信息 * @return 编号 */ - Long createProductThingModel(@Valid IotProductThingModelSaveReqVO createReqVO); + Long createThingModel(@Valid IotThingModelSaveReqVO createReqVO); /** * 更新产品物模型 * * @param updateReqVO 更新信息 */ - void updateProductThingModel(@Valid IotProductThingModelSaveReqVO updateReqVO); + void updateThingModel(@Valid IotThingModelSaveReqVO updateReqVO); /** * 删除产品物模型 * * @param id 编号 */ - void deleteProductThingModel(Long id); + void deleteThingModel(Long id); /** * 获得产品物模型 @@ -43,7 +43,7 @@ public interface IotProductThingModelService { * @param id 编号 * @return 产品物模型 */ - IotProductThingModelDO getProductThingModel(Long id); + IotThingModelDO getThingModel(Long id); /** * 获得产品物模型列表 @@ -51,7 +51,7 @@ public interface IotProductThingModelService { * @param productId 产品编号 * @return 产品物模型列表 */ - List getProductThingModelListByProductId(Long productId); + List getThingModelListByProductId(Long productId); /** * 获得产品物模型分页 @@ -59,14 +59,7 @@ public interface IotProductThingModelService { * @param pageReqVO 分页查询 * @return 产品物模型分页 */ - PageResult getProductThingModelPage(IotProductThingModelPageReqVO pageReqVO); - - /** - * 创建超级表数据模型 - * - * @param productId 产品编号 - */ - void createSuperTableDataModel(Long productId); + PageResult getProductThingModelPage(IotThingModelPageReqVO pageReqVO); /** * 获得产品物模型列表 @@ -74,6 +67,6 @@ public interface IotProductThingModelService { * @param productKey 产品 Key * @return 产品物模型列表 */ - List getProductThingModelListByProductKey(String productKey); + List getProductThingModelListByProductKey(String productKey); } \ No newline at end of file diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/thingmodel/IotThingModelServiceImpl.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/thingmodel/IotThingModelServiceImpl.java new file mode 100644 index 000000000..025c9bca9 --- /dev/null +++ b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/thingmodel/IotThingModelServiceImpl.java @@ -0,0 +1,330 @@ +package cn.iocoder.yudao.module.iot.service.thingmodel; + +import cn.hutool.core.collection.CollUtil; +import cn.hutool.core.util.ObjectUtil; +import cn.iocoder.yudao.framework.common.pojo.PageResult; +import cn.iocoder.yudao.framework.common.util.object.BeanUtils; +import cn.iocoder.yudao.module.iot.controller.admin.thingmodel.model.ThingModelEvent; +import cn.iocoder.yudao.module.iot.controller.admin.thingmodel.model.ThingModelParam; +import cn.iocoder.yudao.module.iot.controller.admin.thingmodel.model.ThingModelService; +import cn.iocoder.yudao.module.iot.controller.admin.thingmodel.vo.IotThingModelPageReqVO; +import cn.iocoder.yudao.module.iot.controller.admin.thingmodel.vo.IotThingModelSaveReqVO; +import cn.iocoder.yudao.module.iot.convert.thingmodel.IotThingModelConvert; +import cn.iocoder.yudao.module.iot.dal.dataobject.product.IotProductDO; +import cn.iocoder.yudao.module.iot.dal.dataobject.thingmodel.IotThingModelDO; +import cn.iocoder.yudao.module.iot.dal.mysql.thingmodel.IotThingModelMapper; +import cn.iocoder.yudao.module.iot.enums.product.IotProductStatusEnum; +import cn.iocoder.yudao.module.iot.enums.thingmodel.*; +import cn.iocoder.yudao.module.iot.service.product.IotProductService; +import jakarta.annotation.Resource; +import lombok.extern.slf4j.Slf4j; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; +import org.springframework.validation.annotation.Validated; + +import java.util.*; + +import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception; +import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.*; +import static cn.iocoder.yudao.module.iot.enums.ErrorCodeConstants.*; + +/** + * IoT 产品物模型 Service 实现类 + * + * @author 芋道源码 + */ +@Service +@Validated +@Slf4j +public class IotThingModelServiceImpl implements IotThingModelService { + + @Resource + private IotThingModelMapper thingModelMapper; + + @Resource + private IotProductService productService; + + @Override + @Transactional(rollbackFor = Exception.class) + public Long createThingModel(IotThingModelSaveReqVO createReqVO) { + // 1. 校验功能标识符在同一产品下是否唯一 + validateIdentifierUnique(createReqVO.getProductId(), createReqVO.getIdentifier()); + + // 2. 功能名称在同一产品下是否唯一 + validateNameUnique(createReqVO.getProductId(), createReqVO.getName()); + + // 3. 系统保留字段,不能用于标识符定义 + validateNotDefaultEventAndService(createReqVO.getIdentifier()); + + // 4. 校验产品状态,发布状态下,不允许新增功能 + validateProductStatus(createReqVO.getProductId()); + + // 5. 插入数据库 + IotThingModelDO thingModel = IotThingModelConvert.INSTANCE.convert(createReqVO); + thingModelMapper.insert(thingModel); + + // 6. 如果创建的是属性,需要更新默认的事件和服务 + if (Objects.equals(createReqVO.getType(), IotThingModelTypeEnum.PROPERTY.getType())) { + createDefaultEventsAndServices(createReqVO.getProductId(), createReqVO.getProductKey()); + } + // TODO @puhui999: 服务和事件的情况 method 怎么设置?在前端设置还是后端设置? + return thingModel.getId(); + } + + @Override + @Transactional(rollbackFor = Exception.class) + public void updateThingModel(IotThingModelSaveReqVO updateReqVO) { + // 1. 校验功能是否存在 + validateProductThingModelMapperExists(updateReqVO.getId()); + + // 2. 校验功能标识符是否唯一 + validateIdentifierUniqueForUpdate(updateReqVO.getId(), updateReqVO.getProductId(), updateReqVO.getIdentifier()); + + // 3. 校验产品状态,发布状态下,不允许操作功能 + validateProductStatus(updateReqVO.getProductId()); + + // 4. 更新数据库 + IotThingModelDO thingModel = IotThingModelConvert.INSTANCE.convert(updateReqVO); + thingModelMapper.updateById(thingModel); + + // 5. 如果更新的是属性,需要更新默认的事件和服务 + if (Objects.equals(updateReqVO.getType(), IotThingModelTypeEnum.PROPERTY.getType())) { + createDefaultEventsAndServices(updateReqVO.getProductId(), updateReqVO.getProductKey()); + } + } + + @Override + @Transactional(rollbackFor = Exception.class) + public void deleteThingModel(Long id) { + // 1. 校验功能是否存在 + IotThingModelDO thingModel = thingModelMapper.selectById(id); + if (thingModel == null) { + throw exception(THING_MODEL_NOT_EXISTS); + } + + // 3. 校验产品状态,发布状态下,不允许操作功能 + validateProductStatus(thingModel.getProductId()); + + // 2. 删除功能 + thingModelMapper.deleteById(id); + + // 3. 如果删除的是属性,需要更新默认的事件和服务 + if (Objects.equals(thingModel.getType(), IotThingModelTypeEnum.PROPERTY.getType())) { + createDefaultEventsAndServices(thingModel.getProductId(), thingModel.getProductKey()); + } + } + + @Override + public IotThingModelDO getThingModel(Long id) { + return thingModelMapper.selectById(id); + } + + @Override + public List getThingModelListByProductId(Long productId) { + return thingModelMapper.selectListByProductId(productId); + } + + @Override + public PageResult getProductThingModelPage(IotThingModelPageReqVO pageReqVO) { + return thingModelMapper.selectPage(pageReqVO); + } + + @Override + public List getProductThingModelListByProductKey(String productKey) { + return thingModelMapper.selectListByProductKey(productKey); + } + + /** + * 校验功能是否存在 + * + * @param id 功能编号 + */ + private void validateProductThingModelMapperExists(Long id) { + if (thingModelMapper.selectById(id) == null) { + throw exception(THING_MODEL_NOT_EXISTS); + } + } + + private void validateIdentifierUniqueForUpdate(Long id, Long productId, String identifier) { + IotThingModelDO thingModel = thingModelMapper.selectByProductIdAndIdentifier(productId, identifier); + if (thingModel != null && ObjectUtil.notEqual(thingModel.getId(), id)) { + throw exception(THING_MODEL_IDENTIFIER_EXISTS); + } + } + + private void validateProductStatus(Long createReqVO) { + IotProductDO product = productService.getProduct(createReqVO); + if (Objects.equals(product.getStatus(), IotProductStatusEnum.PUBLISHED.getStatus())) { + throw exception(PRODUCT_STATUS_NOT_ALLOW_THING_MODEL); + } + } + + private void validateNotDefaultEventAndService(String identifier) { + // set, get, post, property, event, time, value 是系统保留字段,不能用于标识符定义 + if (CollUtil.containsAny(Arrays.asList("set", "get", "post", "property", "event", "time", "value"), Collections.singletonList(identifier))) { + throw exception(THING_MODEL_IDENTIFIER_INVALID); + } + } + + private void validateNameUnique(Long productId, String name) { + IotThingModelDO thingModel = thingModelMapper.selectByProductIdAndName(productId, name); + if (thingModel != null) { + throw exception(THING_MODEL_NAME_EXISTS); + } + } + + private void validateIdentifierUnique(Long productId, String identifier) { + IotThingModelDO thingModel = thingModelMapper.selectByProductIdAndIdentifier(productId, identifier); + if (thingModel != null) { + throw exception(THING_MODEL_IDENTIFIER_EXISTS); + } + } + + /** + * 创建默认的事件和服务 + */ + public void createDefaultEventsAndServices(Long productId, String productKey) { + // 1. 获取当前属性列表 + List properties = thingModelMapper + .selectListByProductIdAndType(productId, IotThingModelTypeEnum.PROPERTY.getType()); + + // 2. 生成新的事件和服务列表 + List newThingModels = new ArrayList<>(); + // 2.1 生成属性上报事件 + ThingModelEvent propertyPostEvent = generatePropertyPostEvent(properties); + if (propertyPostEvent != null) { + newThingModels.add(buildEventThingModelDO(productId, productKey, propertyPostEvent, "属性上报事件")); + } + // 2.2 生成属性设置服务 + ThingModelService propertySetService = generatePropertySetService(properties); + if (propertySetService != null) { + newThingModels.add(buildServiceThingModelDO(productId, productKey, propertySetService, "属性设置服务")); + } + // 2.3 生成属性获取服务 + ThingModelService propertyGetService = generatePropertyGetService(properties); + if (propertyGetService != null) { + newThingModels.add(buildServiceThingModelDO(productId, productKey, propertyGetService, "属性获取服务")); + } + + // 3.1 获取数据库中的默认的旧事件和服务列表 + List oldThingModels = thingModelMapper.selectListByProductIdAndIdentifiersAndTypes( + productId, + Arrays.asList("post", "set", "get"), + Arrays.asList(IotThingModelTypeEnum.EVENT.getType(), IotThingModelTypeEnum.SERVICE.getType()) + ); + // 3.2 创建默认的事件和服务 + createDefaultEventsAndServices(oldThingModels, newThingModels); + } + + /** + * 创建默认的事件和服务 + */ + private void createDefaultEventsAndServices(List oldThingModels, + List newThingModels) { + // 使用 diffList 方法比较新旧列表 + List> diffResult = diffList(oldThingModels, newThingModels, + (oldVal, newVal) -> { + // 继续使用 identifier 和 type 进行比较:这样可以准确地匹配对应的功能对象。 + boolean same = Objects.equals(oldVal.getIdentifier(), newVal.getIdentifier()) + && Objects.equals(oldVal.getType(), newVal.getType()); + if (same) { + newVal.setId(oldVal.getId()); // 设置编号 + } + return same; + }); + // 批量添加、修改、删除 + if (CollUtil.isNotEmpty(diffResult.get(0))) { + thingModelMapper.insertBatch(diffResult.get(0)); + } + if (CollUtil.isNotEmpty(diffResult.get(1))) { + thingModelMapper.updateBatch(diffResult.get(1)); + } + if (CollUtil.isNotEmpty(diffResult.get(2))) { + thingModelMapper.deleteByIds(convertSet(diffResult.get(2), IotThingModelDO::getId)); + } + } + + /** + * 构建事件功能对象 + */ + private IotThingModelDO buildEventThingModelDO(Long productId, String productKey, + ThingModelEvent event, String description) { + return new IotThingModelDO().setProductId(productId).setProductKey(productKey) + .setIdentifier(event.getIdentifier()).setName(event.getName()).setDescription(description) + .setType(IotThingModelTypeEnum.EVENT.getType()).setEvent(event); + } + + /** + * 构建服务功能对象 + */ + private IotThingModelDO buildServiceThingModelDO(Long productId, String productKey, + ThingModelService service, String description) { + return new IotThingModelDO().setProductId(productId).setProductKey(productKey) + .setIdentifier(service.getIdentifier()).setName(service.getName()).setDescription(description) + .setType(IotThingModelTypeEnum.SERVICE.getType()).setService(service); + } + + /** + * 生成属性上报事件 + */ + private ThingModelEvent generatePropertyPostEvent(List thingModels) { + // 没有属性则不生成 + if (CollUtil.isEmpty(thingModels)) { + return null; + } + + // 生成属性上报事件 + return new ThingModelEvent().setIdentifier("post").setName("属性上报").setMethod("thing.event.property.post") + .setType(IotThingModelServiceEventTypeEnum.INFO.getType()) + .setOutputParams(buildInputOutputParam(thingModels, IotThingModelParamDirectionEnum.OUTPUT)); + } + + /** + * 生成属性设置服务 + */ + private ThingModelService generatePropertySetService(List thingModels) { + // 1.1 过滤出所有可写属性 + thingModels = filterList(thingModels, thingModel -> + IotThingModelAccessModeEnum.READ_WRITE.getMode().equals(thingModel.getProperty().getAccessMode())); + // 1.2 没有可写属性则不生成 + if (CollUtil.isEmpty(thingModels)) { + return null; + } + + // 2. 生成属性设置服务 + return new ThingModelService().setIdentifier("set").setName("属性设置").setMethod("thing.service.property.set") + .setCallType(IotThingModelServiceCallTypeEnum.ASYNC.getType()) + .setInputParams(buildInputOutputParam(thingModels, IotThingModelParamDirectionEnum.INPUT)) + .setOutputParams(Collections.emptyList()); // 属性设置服务一般不需要输出参数 + } + + /** + * 生成属性获取服务 + */ + private ThingModelService generatePropertyGetService(List thingModels) { + // 1.1 没有属性则不生成 + if (CollUtil.isEmpty(thingModels)) { + return null; + } + + // 1.2 生成属性获取服务 + return new ThingModelService().setIdentifier("get").setName("属性获取").setMethod("thing.service.property.get") + .setCallType(IotThingModelServiceCallTypeEnum.ASYNC.getType()) + .setInputParams(buildInputOutputParam(thingModels, IotThingModelParamDirectionEnum.INPUT)) + .setOutputParams(buildInputOutputParam(thingModels, IotThingModelParamDirectionEnum.OUTPUT)); + } + + /** + * 构建输入/输出参数列表 + * + * @param thingModels 属性列表 + * @return 输入/输出参数列表 + */ + private List buildInputOutputParam(List thingModels, + IotThingModelParamDirectionEnum direction) { + return convertList(thingModels, thingModel -> + BeanUtils.toBean(thingModel.getProperty(), ThingModelParam.class).setParaOrder(0) // TODO @puhui999: 先搞个默认值看看怎么个事 + .setDirection(direction.getDirection())); + } + +} diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/util/IotTdDatabaseUtils.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/util/IotTdDatabaseUtils.java index e4ecf7465..a409c8069 100644 --- a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/util/IotTdDatabaseUtils.java +++ b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/util/IotTdDatabaseUtils.java @@ -1,13 +1,9 @@ package cn.iocoder.yudao.module.iot.util; +import cn.hutool.core.lang.Assert; import cn.hutool.core.util.StrUtil; import cn.iocoder.yudao.module.iot.enums.IotConstants; import cn.iocoder.yudao.module.iot.enums.product.IotProductDeviceTypeEnum; -import org.springframework.beans.factory.annotation.Value; -import org.springframework.stereotype.Component; - -import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception; -import static cn.iocoder.yudao.module.iot.enums.ErrorCodeConstants.PRODUCT_DEVICE_NOT_EXISTS; // TODO @芋艿:可能要思索下,有没更好的处理方式 // TODO @芋艿:怎么改成无状态 @@ -34,20 +30,17 @@ public class IotTdDatabaseUtils { * @return 产品超级表表名 */ public static String getProductSuperTableName(Integer deviceType, String productKey) { - // TODO @alwayssuper:枚举字段,不要 1、2、3;不符合预期,抛出异常 - if (deviceType == null) { - throw exception(PRODUCT_DEVICE_NOT_EXISTS); - } + Assert.notNull(deviceType, "deviceType 不能为空"); if (IotProductDeviceTypeEnum.GATEWAY_SUB.getType().equals(deviceType)) { return String.format(IotConstants.GATEWAY_SUB_STABLE_NAME_FORMAT, productKey).toLowerCase(); - } else if (IotProductDeviceTypeEnum.GATEWAY.getType().equals(deviceType)) { + } + if (IotProductDeviceTypeEnum.GATEWAY.getType().equals(deviceType)) { return String.format(IotConstants.GATEWAY_STABLE_NAME_FORMAT, productKey).toLowerCase(); - } else if (IotProductDeviceTypeEnum.DIRECT.getType().equals(deviceType)){ + } + if (IotProductDeviceTypeEnum.DIRECT.getType().equals(deviceType)){ return String.format(IotConstants.DEVICE_STABLE_NAME_FORMAT, productKey).toLowerCase(); } - else{ - throw exception(PRODUCT_DEVICE_NOT_EXISTS); - } + throw new IllegalArgumentException("deviceType 不正确"); } /** @@ -58,7 +51,6 @@ public class IotTdDatabaseUtils { * */ public static String getThingModelMessageSuperTableName(String productKey) { - // TODO @alwayssuper:是不是应该 + 拼接就好,不用 format return "thing_model_message_" + productKey.toLowerCase(); } @@ -70,7 +62,8 @@ public class IotTdDatabaseUtils { * @return 物模型日志设备表名 */ public static String getThingModelMessageDeviceTableName(String productKey, String deviceName) { - return String.format(IotConstants.THING_MODEL_MESSAGE_TABLE_NAME_FORMAT, productKey.toLowerCase(), deviceName.toLowerCase()); + return String.format(IotConstants.THING_MODEL_MESSAGE_TABLE_NAME_FORMAT, + productKey.toLowerCase(), deviceName.toLowerCase()); } } diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/resources/mapper/device/IotDevicePropertyDataMapper.xml b/yudao-module-iot/yudao-module-iot-biz/src/main/resources/mapper/device/IotDevicePropertyDataMapper.xml new file mode 100644 index 000000000..9894e27a0 --- /dev/null +++ b/yudao-module-iot/yudao-module-iot-biz/src/main/resources/mapper/device/IotDevicePropertyDataMapper.xml @@ -0,0 +1,45 @@ + + + + + + + + CREATE STABLE product_property_${productKey} + + ${field.field} ${field.type} + + (${field.length}) + + + TAGS ( + device_key NCHAR(50) + ) + + + + ALTER STABLE product_property_${productKey} + ADD COLUMN ${field.field} ${field.type} + + (${field.length}) + + + + + ALTER STABLE product_property_${productKey} + MODIFY COLUMN ${field.field} ${field.type} + + (${field.length}) + + + + + ALTER STABLE product_property_${productKey} + DROP COLUMN ${field.field} + + + \ No newline at end of file diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/resources/mapper/plugininstance/PluginInstanceMapper.xml b/yudao-module-iot/yudao-module-iot-biz/src/main/resources/mapper/plugininstance/PluginInstanceMapper.xml index 2d297c785..007a417ed 100644 --- a/yudao-module-iot/yudao-module-iot-biz/src/main/resources/mapper/plugininstance/PluginInstanceMapper.xml +++ b/yudao-module-iot/yudao-module-iot-biz/src/main/resources/mapper/plugininstance/PluginInstanceMapper.xml @@ -1,6 +1,6 @@ - + - CREATE STABLE ${dataBaseName}.${superTableName}( ts TIMESTAMP, diff --git a/yudao-module-iot/yudao-module-iot-plugin/yudao-module-iot-http-plugin/pom.xml b/yudao-module-iot/yudao-module-iot-plugin/yudao-module-iot-http-plugin/pom.xml index 200a451b6..1ecf140a4 100644 --- a/yudao-module-iot/yudao-module-iot-plugin/yudao-module-iot-http-plugin/pom.xml +++ b/yudao-module-iot/yudao-module-iot-plugin/yudao-module-iot-http-plugin/pom.xml @@ -24,6 +24,7 @@ cn.iocoder.yudao.module.iot.plugin.HttpPlugin 0.0.1 ahh + http-plugin-0.0.1 @@ -104,6 +105,7 @@ ${plugin.class} ${plugin.version} ${plugin.provider} + ${plugin.description} ${plugin.dependencies} diff --git a/yudao-module-iot/yudao-module-iot-plugin/yudao-module-iot-http-plugin/src/main/java/cn/iocoder/yudao/module/iot/plugin/HttpHandler.java b/yudao-module-iot/yudao-module-iot-plugin/yudao-module-iot-http-plugin/src/main/java/cn/iocoder/yudao/module/iot/plugin/HttpHandler.java index c145182ed..6d0908683 100644 --- a/yudao-module-iot/yudao-module-iot-plugin/yudao-module-iot-http-plugin/src/main/java/cn/iocoder/yudao/module/iot/plugin/HttpHandler.java +++ b/yudao-module-iot/yudao-module-iot-plugin/yudao-module-iot-http-plugin/src/main/java/cn/iocoder/yudao/module/iot/plugin/HttpHandler.java @@ -2,7 +2,7 @@ package cn.iocoder.yudao.module.iot.plugin; import cn.hutool.json.JSONObject; import cn.hutool.json.JSONUtil; -import cn.iocoder.yudao.module.iot.api.DeviceDataApi; +import cn.iocoder.yudao.module.iot.api.device.DeviceDataApi; import io.netty.buffer.Unpooled; import io.netty.channel.ChannelFutureListener; import io.netty.channel.ChannelHandlerContext; @@ -12,12 +12,9 @@ import io.netty.util.CharsetUtil; /** * 基于 Netty 的 HTTP 处理器,用于接收设备上报的数据并调用主程序的 DeviceDataApi 接口进行处理。 - *

- * 请求格式: - * POST /sys/{productKey}/{deviceName}/thing/event/property/post - * 请求体为 JSON 格式数据。 - *

- * 返回结果为 JSON 格式,包含统一的 code、data、id、message、method、version 字段。 + * + * 1. 请求格式:JSON 格式,地址为 POST /sys/{productKey}/{deviceName}/thing/event/property/post + * 2. 返回结果:JSON 格式,包含统一的 code、data、id、message、method、version 字段 */ public class HttpHandler extends SimpleChannelInboundHandler { @@ -29,10 +26,9 @@ public class HttpHandler extends SimpleChannelInboundHandler { @Override protected void channelRead0(ChannelHandlerContext ctx, FullHttpRequest request) { - String uri = request.uri(); - // 期望的路径格式: /sys/{productKey}/{deviceName}/thing/event/property/post // 使用 "/" 拆分路径 + String uri = request.uri(); String[] parts = uri.split("/"); /* @@ -52,25 +48,19 @@ public class HttpHandler extends SimpleChannelInboundHandler { && "event".equals(parts[5]) && "property".equals(parts[6]) && "post".equals(parts[7]); - if (!isCorrectPath) { - // 如果路径不匹配,返回 404 错误 writeResponse(ctx, HttpResponseStatus.NOT_FOUND, "Not Found"); return; } - String productKey = parts[2]; String deviceName = parts[3]; - // 从请求中获取原始数据 + // 从请求中获取原始数据,尝试解析请求数据为 JSON 对象 String requestBody = request.content().toString(CharsetUtil.UTF_8); - - // 尝试解析请求数据为 JSON 对象 JSONObject jsonData; try { jsonData = JSONUtil.parseObj(requestBody); } catch (Exception e) { - // 数据不是合法的 JSON 格式,返回 400 错误 JSONObject res = createResponseJson( 400, new JSONObject(), @@ -82,8 +72,6 @@ public class HttpHandler extends SimpleChannelInboundHandler { writeResponse(ctx, HttpResponseStatus.BAD_REQUEST, res.toString()); return; } - - // 获取请求中的 id 字段,若不存在则为 null String id = jsonData.getStr("id", null); try { @@ -101,7 +89,6 @@ public class HttpHandler extends SimpleChannelInboundHandler { ); writeResponse(ctx, HttpResponseStatus.OK, successRes.toString()); } catch (Exception e) { - // 保存数据过程中出现异常,返回 500 错误 JSONObject errorRes = createResponseJson( 500, new JSONObject(), @@ -128,7 +115,7 @@ public class HttpHandler extends SimpleChannelInboundHandler { private JSONObject createResponseJson(int code, JSONObject data, String id, String message, String method, String version) { JSONObject res = new JSONObject(); res.set("code", code); - res.set("data", data != null ? data : new JSONObject()); // 确保 data 不为 null + res.set("data", data != null ? data : new JSONObject()); res.set("id", id); res.set("message", message); res.set("method", method); @@ -137,24 +124,24 @@ public class HttpHandler extends SimpleChannelInboundHandler { } /** - * 向客户端返回 HTTP 响应的辅助方法。 + * 向客户端返回 HTTP 响应的辅助方法 * * @param ctx 通道上下文 * @param status HTTP 响应状态码(网络层面的) * @param content 响应内容(JSON 字符串或其他文本) */ private void writeResponse(ChannelHandlerContext ctx, HttpResponseStatus status, String content) { + // 设置响应头为 JSON 类型和正确的编码 FullHttpResponse response = new DefaultFullHttpResponse( HttpVersion.HTTP_1_1, status, Unpooled.copiedBuffer(content, CharsetUtil.UTF_8) ); - - // 设置响应头为 JSON 类型和正确的编码 response.headers().set(HttpHeaderNames.CONTENT_TYPE, "application/json; charset=UTF-8"); response.headers().set(HttpHeaderNames.CONTENT_LENGTH, response.content().readableBytes()); // 发送响应并在发送完成后关闭连接 ctx.writeAndFlush(response).addListener(ChannelFutureListener.CLOSE); } + } \ No newline at end of file diff --git a/yudao-module-iot/yudao-module-iot-plugin/yudao-module-iot-http-plugin/src/main/java/cn/iocoder/yudao/module/iot/plugin/HttpPlugin.java b/yudao-module-iot/yudao-module-iot-plugin/yudao-module-iot-http-plugin/src/main/java/cn/iocoder/yudao/module/iot/plugin/HttpPlugin.java index 68311300e..66e0c69a3 100644 --- a/yudao-module-iot/yudao-module-iot-plugin/yudao-module-iot-http-plugin/src/main/java/cn/iocoder/yudao/module/iot/plugin/HttpPlugin.java +++ b/yudao-module-iot/yudao-module-iot-plugin/yudao-module-iot-http-plugin/src/main/java/cn/iocoder/yudao/module/iot/plugin/HttpPlugin.java @@ -1,6 +1,6 @@ package cn.iocoder.yudao.module.iot.plugin; -import cn.iocoder.yudao.module.iot.api.DeviceDataApi; +import cn.iocoder.yudao.module.iot.api.device.DeviceDataApi; import cn.iocoder.yudao.module.iot.api.ServiceRegistry; import io.netty.bootstrap.ServerBootstrap; import io.netty.channel.*; @@ -18,18 +18,25 @@ import java.util.concurrent.Executors; public class HttpPlugin extends Plugin { private static final int PORT = 8092; - private final ExecutorService executorService; - private DeviceDataApi deviceDataApi; // 用于保存设备数据的 API + + private ExecutorService executorService; + private DeviceDataApi deviceDataApi; public HttpPlugin(PluginWrapper wrapper) { super(wrapper); - this.executorService = Executors.newSingleThreadExecutor(); // 创建单线程池 + // 初始化线程池 + this.executorService = Executors.newSingleThreadExecutor(); } @Override public void start() { log.info("HttpPlugin.start()"); + // 重新初始化线程池,确保它是活跃的 + if (executorService.isShutdown() || executorService.isTerminated()) { + executorService = Executors.newSingleThreadExecutor(); + } + // 从 ServiceRegistry 中获取主程序暴露的 DeviceDataApi 接口实例 deviceDataApi = ServiceRegistry.getService(DeviceDataApi.class); if (deviceDataApi == null) { @@ -44,10 +51,13 @@ public class HttpPlugin extends Plugin { @Override public void stop() { log.info("HttpPlugin.stop()"); - executorService.shutdownNow(); // 停止线程池 + // 停止线程池 + executorService.shutdownNow(); } - // 启动 HTTP 服务 + /** + * 启动 HTTP 服务 + */ private void startHttpServer() { EventLoopGroup bossGroup = new NioEventLoopGroup(1); EventLoopGroup workerGroup = new NioEventLoopGroup(); @@ -56,7 +66,8 @@ public class HttpPlugin extends Plugin { ServerBootstrap bootstrap = new ServerBootstrap(); bootstrap.group(bossGroup, workerGroup) .channel(NioServerSocketChannel.class) - .childHandler(new ChannelInitializer() { + .childHandler(new ChannelInitializer<>() { + @Override protected void initChannel(Channel channel) { channel.pipeline().addLast(new HttpServerCodec()); @@ -64,6 +75,7 @@ public class HttpPlugin extends Plugin { // 将从 ServiceRegistry 获取的 deviceDataApi 传入处理器 channel.pipeline().addLast(new HttpHandler(deviceDataApi)); } + }); // 绑定端口并启动服务器 @@ -72,10 +84,11 @@ public class HttpPlugin extends Plugin { future.channel().closeFuture().sync(); } catch (InterruptedException e) { Thread.currentThread().interrupt(); - log.error("HTTP 服务启动中断", e); + log.warn("HTTP 服务启动被中断", e); } finally { bossGroup.shutdownGracefully(); workerGroup.shutdownGracefully(); } } -} + +} \ No newline at end of file