Commit 1ce9420a8d77a68595ce319ac80ef7fd5f96d2c0
Merge branch 'feature/iot' of https://gitee.com/zhijiantianya/ruoyi-vue-pro into origin/feature/iot
Showing
97 changed files
with
1648 additions
and
1787 deletions
plugins/enabled.txt
plugins/yudao-module-iot-http-plugin-2.2.0-snapshot.jar
No preview for this file type
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 { | @@ -66,7 +66,9 @@ public class CrmReceivableDO extends BaseDO { | ||
| 66 | */ | 66 | */ |
| 67 | private LocalDateTime returnTime; | 67 | private LocalDateTime returnTime; |
| 68 | /** | 68 | /** |
| 69 | - * 回款方式,关联枚举{@link CrmReceivableReturnTypeEnum} | 69 | + * 回款方式 |
| 70 | + * | ||
| 71 | + * 枚举 {@link CrmReceivableReturnTypeEnum} | ||
| 70 | */ | 72 | */ |
| 71 | private Integer returnType; | 73 | private Integer returnType; |
| 72 | /** | 74 | /** |
yudao-module-iot/yudao-module-iot-api/pom.xml
| @@ -22,6 +22,7 @@ | @@ -22,6 +22,7 @@ | ||
| 22 | <artifactId>yudao-common</artifactId> | 22 | <artifactId>yudao-common</artifactId> |
| 23 | </dependency> | 23 | </dependency> |
| 24 | <!-- PF4J --> | 24 | <!-- PF4J --> |
| 25 | + <!-- TODO 芋艿:这个依赖,要不要放在 api 包 --> | ||
| 25 | <dependency> | 26 | <dependency> |
| 26 | <groupId>org.pf4j</groupId> | 27 | <groupId>org.pf4j</groupId> |
| 27 | <artifactId>pf4j-spring</artifactId> | 28 | <artifactId>pf4j-spring</artifactId> |
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; | @@ -7,6 +7,7 @@ import java.util.Map; | ||
| 7 | * 服务注册表 - 插架模块使用,无法使用 Spring 注入 | 7 | * 服务注册表 - 插架模块使用,无法使用 Spring 注入 |
| 8 | */ | 8 | */ |
| 9 | public class ServiceRegistry { | 9 | public class ServiceRegistry { |
| 10 | + | ||
| 10 | private static final Map<Class<?>, Object> services = new HashMap<>(); | 11 | private static final Map<Class<?>, Object> services = new HashMap<>(); |
| 11 | 12 | ||
| 12 | /** | 13 | /** |
| @@ -31,4 +32,5 @@ public class ServiceRegistry { | @@ -31,4 +32,5 @@ public class ServiceRegistry { | ||
| 31 | public static <T> T getService(Class<T> serviceClass) { | 32 | public static <T> T getService(Class<T> serviceClass) { |
| 32 | return (T) services.get(serviceClass); | 33 | return (T) services.get(serviceClass); |
| 33 | } | 34 | } |
| 35 | + | ||
| 34 | } | 36 | } |
| 35 | \ No newline at end of file | 37 | \ No newline at end of file |
yudao-module-iot/yudao-module-iot-api/src/main/java/cn/iocoder/yudao/module/iot/api/DeviceDataApi.java renamed to yudao-module-iot/yudao-module-iot-api/src/main/java/cn/iocoder/yudao/module/iot/api/device/DeviceDataApi.java
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 { | @@ -14,7 +14,6 @@ public interface ErrorCodeConstants { | ||
| 14 | ErrorCode PRODUCT_KEY_EXISTS = new ErrorCode(1_050_001_001, "产品标识已经存在"); | 14 | ErrorCode PRODUCT_KEY_EXISTS = new ErrorCode(1_050_001_001, "产品标识已经存在"); |
| 15 | ErrorCode PRODUCT_STATUS_NOT_DELETE = new ErrorCode(1_050_001_002, "产品状是发布状态,不允许删除"); | 15 | ErrorCode PRODUCT_STATUS_NOT_DELETE = new ErrorCode(1_050_001_002, "产品状是发布状态,不允许删除"); |
| 16 | ErrorCode PRODUCT_STATUS_NOT_ALLOW_THING_MODEL = new ErrorCode(1_050_001_003, "产品状是发布状态,不允许操作物模型"); | 16 | ErrorCode PRODUCT_STATUS_NOT_ALLOW_THING_MODEL = new ErrorCode(1_050_001_003, "产品状是发布状态,不允许操作物模型"); |
| 17 | - ErrorCode PRODUCT_DEVICE_NOT_EXISTS = new ErrorCode(1_050_001_004, "产品设备类型不存在"); | ||
| 18 | 17 | ||
| 19 | // ========== 产品物模型 1-050-002-000 ============ | 18 | // ========== 产品物模型 1-050-002-000 ============ |
| 20 | ErrorCode THING_MODEL_NOT_EXISTS = new ErrorCode(1_050_002_000, "产品物模型不存在"); | 19 | ErrorCode THING_MODEL_NOT_EXISTS = new ErrorCode(1_050_002_000, "产品物模型不存在"); |
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 { | @@ -34,12 +34,10 @@ public enum IotPluginDeployTypeEnum implements IntArrayValuable { | ||
| 34 | } | 34 | } |
| 35 | 35 | ||
| 36 | public static IotPluginDeployTypeEnum fromDeployType(Integer deployType) { | 36 | public static IotPluginDeployTypeEnum fromDeployType(Integer deployType) { |
| 37 | - for (IotPluginDeployTypeEnum value : values()) { | ||
| 38 | - if (value.getDeployType().equals(deployType)) { | ||
| 39 | - return value; | ||
| 40 | - } | ||
| 41 | - } | ||
| 42 | - return null; | 37 | + return Arrays.stream(values()) |
| 38 | + .filter(value -> value.getDeployType().equals(deployType)) | ||
| 39 | + .findFirst() | ||
| 40 | + .orElse(null); | ||
| 43 | } | 41 | } |
| 44 | 42 | ||
| 45 | public static boolean isValidDeployType(Integer deployType) { | 43 | public static boolean isValidDeployType(Integer deployType) { |
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 { | @@ -34,12 +34,10 @@ public enum IotPluginStatusEnum implements IntArrayValuable { | ||
| 34 | } | 34 | } |
| 35 | 35 | ||
| 36 | public static IotPluginStatusEnum fromState(Integer state) { | 36 | public static IotPluginStatusEnum fromState(Integer state) { |
| 37 | - for (IotPluginStatusEnum value : values()) { | ||
| 38 | - if (value.getStatus().equals(state)) { | ||
| 39 | - return value; | ||
| 40 | - } | ||
| 41 | - } | ||
| 42 | - return null; | 37 | + return Arrays.stream(values()) |
| 38 | + .filter(value -> value.getStatus().equals(state)) | ||
| 39 | + .findFirst() | ||
| 40 | + .orElse(null); | ||
| 43 | } | 41 | } |
| 44 | 42 | ||
| 45 | @Override | 43 | @Override |
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 { | @@ -35,14 +35,11 @@ public enum IotPluginTypeEnum implements IntArrayValuable { | ||
| 35 | return ARRAYS; | 35 | return ARRAYS; |
| 36 | } | 36 | } |
| 37 | 37 | ||
| 38 | - // TODO @haohao:可以使用 hutool 简化 | ||
| 39 | public static IotPluginTypeEnum fromType(Integer type) { | 38 | public static IotPluginTypeEnum fromType(Integer type) { |
| 40 | - for (IotPluginTypeEnum value : values()) { | ||
| 41 | - if (value.getType().equals(type)) { | ||
| 42 | - return value; | ||
| 43 | - } | ||
| 44 | - } | ||
| 45 | - return null; | 39 | + return Arrays.stream(values()) |
| 40 | + .filter(value -> value.getType().equals(type)) | ||
| 41 | + .findFirst() | ||
| 42 | + .orElse(null); | ||
| 46 | } | 43 | } |
| 47 | 44 | ||
| 48 | public static boolean isValidType(Integer type) { | 45 | public static boolean isValidType(Integer type) { |
yudao-module-iot/yudao-module-iot-api/src/main/java/cn/iocoder/yudao/module/iot/enums/thingmodel/IotDataSpecsDataTypeEnum.java
0 → 100644
| 1 | +package cn.iocoder.yudao.module.iot.enums.thingmodel; | ||
| 2 | + | ||
| 3 | +import lombok.AllArgsConstructor; | ||
| 4 | +import lombok.Getter; | ||
| 5 | + | ||
| 6 | +/** | ||
| 7 | + * IoT 数据定义的数据类型枚举类 | ||
| 8 | + * | ||
| 9 | + * @author 芋道源码 | ||
| 10 | + */ | ||
| 11 | +@AllArgsConstructor | ||
| 12 | +@Getter | ||
| 13 | +public enum IotDataSpecsDataTypeEnum { | ||
| 14 | + | ||
| 15 | + INT("int"), | ||
| 16 | + FLOAT("float"), | ||
| 17 | + DOUBLE("double"), | ||
| 18 | + ENUM("enum"), | ||
| 19 | + BOOL("bool"), | ||
| 20 | + TEXT("text"), | ||
| 21 | + DATE("date"), | ||
| 22 | + STRUCT("struct"), | ||
| 23 | + ARRAY("array"); | ||
| 24 | + | ||
| 25 | + private final String dataType; | ||
| 26 | + | ||
| 27 | +} |
yudao-module-iot/yudao-module-iot-api/src/main/java/cn/iocoder/yudao/module/iot/enums/thingmodel/IotProductThingModelAccessModeEnum.java renamed to 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; | @@ -4,13 +4,13 @@ import lombok.AllArgsConstructor; | ||
| 4 | import lombok.Getter; | 4 | import lombok.Getter; |
| 5 | 5 | ||
| 6 | /** | 6 | /** |
| 7 | - * IOT 访问方式枚举类 | 7 | + * IOT 产品物模型属性读取类型枚举 |
| 8 | * | 8 | * |
| 9 | * @author ahh | 9 | * @author ahh |
| 10 | */ | 10 | */ |
| 11 | @AllArgsConstructor | 11 | @AllArgsConstructor |
| 12 | @Getter | 12 | @Getter |
| 13 | -public enum IotProductThingModelAccessModeEnum { | 13 | +public enum IotThingModelAccessModeEnum { |
| 14 | 14 | ||
| 15 | READ_ONLY("r"), | 15 | READ_ONLY("r"), |
| 16 | READ_WRITE("rw"); | 16 | READ_WRITE("rw"); |
yudao-module-iot/yudao-module-iot-api/src/main/java/cn/iocoder/yudao/module/iot/enums/thingmodel/IotThingModelParamDirectionEnum.java
0 → 100644
| 1 | +package cn.iocoder.yudao.module.iot.enums.thingmodel; | ||
| 2 | + | ||
| 3 | +import lombok.AllArgsConstructor; | ||
| 4 | +import lombok.Getter; | ||
| 5 | + | ||
| 6 | +/** | ||
| 7 | + * IOT 产品物模型参数是输入参数还是输出参数枚举 | ||
| 8 | + * | ||
| 9 | + * @author HUIHUI | ||
| 10 | + */ | ||
| 11 | +@AllArgsConstructor | ||
| 12 | +@Getter | ||
| 13 | +public enum IotThingModelParamDirectionEnum { | ||
| 14 | + | ||
| 15 | + INPUT("input"), // 输入参数 | ||
| 16 | + OUTPUT("output"); // 输出参数 | ||
| 17 | + | ||
| 18 | + private final String direction; | ||
| 19 | + | ||
| 20 | +} |
yudao-module-iot/yudao-module-iot-api/src/main/java/cn/iocoder/yudao/module/iot/enums/thingmodel/IotThingModelServiceCallTypeEnum.java
0 → 100644
| 1 | +package cn.iocoder.yudao.module.iot.enums.thingmodel; | ||
| 2 | + | ||
| 3 | +import lombok.AllArgsConstructor; | ||
| 4 | +import lombok.Getter; | ||
| 5 | + | ||
| 6 | +/** | ||
| 7 | + * IOT 产品物模型服务调用方式枚举 | ||
| 8 | + * | ||
| 9 | + * @author HUIHUI | ||
| 10 | + */ | ||
| 11 | +@AllArgsConstructor | ||
| 12 | +@Getter | ||
| 13 | +public enum IotThingModelServiceCallTypeEnum { | ||
| 14 | + | ||
| 15 | + ASYNC("async"), // 异步调用 | ||
| 16 | + SYNC("sync"); // 同步调用 | ||
| 17 | + | ||
| 18 | + private final String type; | ||
| 19 | + | ||
| 20 | +} |
yudao-module-iot/yudao-module-iot-api/src/main/java/cn/iocoder/yudao/module/iot/enums/thingmodel/IotThingModelServiceEventTypeEnum.java
0 → 100644
| 1 | +package cn.iocoder.yudao.module.iot.enums.thingmodel; | ||
| 2 | + | ||
| 3 | +import lombok.AllArgsConstructor; | ||
| 4 | +import lombok.Getter; | ||
| 5 | + | ||
| 6 | +/** | ||
| 7 | + * IOT 产品物模型事件类型枚举 | ||
| 8 | + * | ||
| 9 | + * @author HUIHUI | ||
| 10 | + */ | ||
| 11 | +@AllArgsConstructor | ||
| 12 | +@Getter | ||
| 13 | +public enum IotThingModelServiceEventTypeEnum { | ||
| 14 | + | ||
| 15 | + INFO("info"), // 信息 | ||
| 16 | + ALERT("alert"), // 告警 | ||
| 17 | + ERROR("error"); // 故障 | ||
| 18 | + | ||
| 19 | + private final String type; | ||
| 20 | + | ||
| 21 | +} |
yudao-module-iot/yudao-module-iot-api/src/main/java/cn/iocoder/yudao/module/iot/enums/thingmodel/IotProductThingModelTypeEnum.java renamed to 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; | @@ -13,13 +13,13 @@ import java.util.Arrays; | ||
| 13 | */ | 13 | */ |
| 14 | @AllArgsConstructor | 14 | @AllArgsConstructor |
| 15 | @Getter | 15 | @Getter |
| 16 | -public enum IotProductThingModelTypeEnum implements IntArrayValuable { | 16 | +public enum IotThingModelTypeEnum implements IntArrayValuable { |
| 17 | 17 | ||
| 18 | PROPERTY(1, "属性"), | 18 | PROPERTY(1, "属性"), |
| 19 | SERVICE(2, "服务"), | 19 | SERVICE(2, "服务"), |
| 20 | EVENT(3, "事件"); | 20 | EVENT(3, "事件"); |
| 21 | 21 | ||
| 22 | - public static final int[] ARRAYS = Arrays.stream(values()).mapToInt(IotProductThingModelTypeEnum::getType).toArray(); | 22 | + public static final int[] ARRAYS = Arrays.stream(values()).mapToInt(IotThingModelTypeEnum::getType).toArray(); |
| 23 | 23 | ||
| 24 | /** | 24 | /** |
| 25 | * 类型 | 25 | * 类型 |
| @@ -30,8 +30,8 @@ public enum IotProductThingModelTypeEnum implements IntArrayValuable { | @@ -30,8 +30,8 @@ public enum IotProductThingModelTypeEnum implements IntArrayValuable { | ||
| 30 | */ | 30 | */ |
| 31 | private final String description; | 31 | private final String description; |
| 32 | 32 | ||
| 33 | - public static IotProductThingModelTypeEnum valueOfType(Integer type) { | ||
| 34 | - for (IotProductThingModelTypeEnum value : values()) { | 33 | + public static IotThingModelTypeEnum valueOfType(Integer type) { |
| 34 | + for (IotThingModelTypeEnum value : values()) { | ||
| 35 | if (value.getType().equals(type)) { | 35 | if (value.getType().equals(type)) { |
| 36 | return value; | 36 | return value; |
| 37 | } | 37 | } |
yudao-module-iot/yudao-module-iot-biz/pom.xml
| @@ -27,13 +27,6 @@ | @@ -27,13 +27,6 @@ | ||
| 27 | 27 | ||
| 28 | <dependency> | 28 | <dependency> |
| 29 | <groupId>cn.iocoder.boot</groupId> | 29 | <groupId>cn.iocoder.boot</groupId> |
| 30 | - <artifactId>yudao-module-iot-plugin-api</artifactId> | ||
| 31 | - <version>0.0.1</version> | ||
| 32 | - <scope>compile</scope> | ||
| 33 | - </dependency> | ||
| 34 | - | ||
| 35 | - <dependency> | ||
| 36 | - <groupId>cn.iocoder.boot</groupId> | ||
| 37 | <artifactId>yudao-spring-boot-starter-biz-tenant</artifactId> | 30 | <artifactId>yudao-spring-boot-starter-biz-tenant</artifactId> |
| 38 | </dependency> | 31 | </dependency> |
| 39 | 32 | ||
| @@ -81,6 +74,7 @@ | @@ -81,6 +74,7 @@ | ||
| 81 | <dependency> | 74 | <dependency> |
| 82 | <groupId>org.pf4j</groupId> | 75 | <groupId>org.pf4j</groupId> |
| 83 | <artifactId>pf4j-spring</artifactId> | 76 | <artifactId>pf4j-spring</artifactId> |
| 77 | + <!-- TODO @芋艿:可以放到 bom 里配置 --> | ||
| 84 | <exclusions> | 78 | <exclusions> |
| 85 | <exclusion> | 79 | <exclusion> |
| 86 | <groupId>org.slf4j</groupId> | 80 | <groupId>org.slf4j</groupId> |
yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/api/device/DeviceDataApiImpl.java
| 1 | package cn.iocoder.yudao.module.iot.api.device; | 1 | package cn.iocoder.yudao.module.iot.api.device; |
| 2 | 2 | ||
| 3 | -import cn.iocoder.yudao.module.iot.api.DeviceDataApi; | ||
| 4 | -import cn.iocoder.yudao.module.iot.service.device.IotDeviceDataService; | 3 | +import cn.iocoder.yudao.module.iot.service.device.IotDevicePropertyDataService; |
| 5 | import org.springframework.stereotype.Service; | 4 | import org.springframework.stereotype.Service; |
| 6 | import org.springframework.validation.annotation.Validated; | 5 | import org.springframework.validation.annotation.Validated; |
| 7 | 6 | ||
| @@ -15,11 +14,11 @@ import javax.annotation.Resource; | @@ -15,11 +14,11 @@ import javax.annotation.Resource; | ||
| 15 | public class DeviceDataApiImpl implements DeviceDataApi { | 14 | public class DeviceDataApiImpl implements DeviceDataApi { |
| 16 | 15 | ||
| 17 | @Resource | 16 | @Resource |
| 18 | - private IotDeviceDataService iotDeviceDataService; | 17 | + private IotDevicePropertyDataService deviceDataService; |
| 19 | 18 | ||
| 20 | @Override | 19 | @Override |
| 21 | public void saveDeviceData(String productKey, String deviceName, String message) { | 20 | public void saveDeviceData(String productKey, String deviceName, String message) { |
| 22 | - iotDeviceDataService.saveDeviceData(productKey, deviceName, message); | 21 | + deviceDataService.saveDeviceData(productKey, deviceName, message); |
| 23 | } | 22 | } |
| 24 | 23 | ||
| 25 | } | 24 | } |
| 26 | \ No newline at end of file | 25 | \ No newline at end of file |
yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/api/package-info.java
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 | @@ -7,7 +7,7 @@ import cn.iocoder.yudao.module.iot.controller.admin.device.vo.deviceData.IotDevi | ||
| 7 | import cn.iocoder.yudao.module.iot.controller.admin.device.vo.deviceData.IotDeviceDataRespVO; | 7 | import cn.iocoder.yudao.module.iot.controller.admin.device.vo.deviceData.IotDeviceDataRespVO; |
| 8 | import cn.iocoder.yudao.module.iot.dal.dataobject.device.IotDeviceDataDO; | 8 | import cn.iocoder.yudao.module.iot.dal.dataobject.device.IotDeviceDataDO; |
| 9 | import cn.iocoder.yudao.module.iot.controller.admin.device.vo.deviceData.IotTimeDataRespVO; | 9 | import cn.iocoder.yudao.module.iot.controller.admin.device.vo.deviceData.IotTimeDataRespVO; |
| 10 | -import cn.iocoder.yudao.module.iot.service.device.IotDeviceDataService; | 10 | +import cn.iocoder.yudao.module.iot.service.device.IotDevicePropertyDataService; |
| 11 | import io.swagger.v3.oas.annotations.Operation; | 11 | import io.swagger.v3.oas.annotations.Operation; |
| 12 | import io.swagger.v3.oas.annotations.tags.Tag; | 12 | import io.swagger.v3.oas.annotations.tags.Tag; |
| 13 | import jakarta.annotation.Resource; | 13 | import jakarta.annotation.Resource; |
| @@ -29,7 +29,7 @@ import static cn.iocoder.yudao.framework.common.pojo.CommonResult.success; | @@ -29,7 +29,7 @@ import static cn.iocoder.yudao.framework.common.pojo.CommonResult.success; | ||
| 29 | public class IotDeviceDataController { | 29 | public class IotDeviceDataController { |
| 30 | 30 | ||
| 31 | @Resource | 31 | @Resource |
| 32 | - private IotDeviceDataService deviceDataService; | 32 | + private IotDevicePropertyDataService deviceDataService; |
| 33 | 33 | ||
| 34 | // TODO @浩浩:这里的 /latest-list,包括方法名。 | 34 | // TODO @浩浩:这里的 /latest-list,包括方法名。 |
| 35 | @GetMapping("/latest") | 35 | @GetMapping("/latest") |
yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/plugininfo/PluginInfoController.java renamed to yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/plugin/PluginInfoController.java
| 1 | -package cn.iocoder.yudao.module.iot.controller.admin.plugininfo; | 1 | +package cn.iocoder.yudao.module.iot.controller.admin.plugin; |
| 2 | 2 | ||
| 3 | import cn.iocoder.yudao.framework.common.pojo.CommonResult; | 3 | import cn.iocoder.yudao.framework.common.pojo.CommonResult; |
| 4 | import cn.iocoder.yudao.framework.common.pojo.PageResult; | 4 | import cn.iocoder.yudao.framework.common.pojo.PageResult; |
| 5 | import cn.iocoder.yudao.framework.common.util.object.BeanUtils; | 5 | import cn.iocoder.yudao.framework.common.util.object.BeanUtils; |
| 6 | -import cn.iocoder.yudao.module.iot.controller.admin.plugininfo.vo.PluginInfoPageReqVO; | ||
| 7 | -import cn.iocoder.yudao.module.iot.controller.admin.plugininfo.vo.PluginInfoRespVO; | ||
| 8 | -import cn.iocoder.yudao.module.iot.controller.admin.plugininfo.vo.PluginInfoSaveReqVO; | 6 | +import cn.iocoder.yudao.module.iot.controller.admin.plugin.vo.PluginInfoImportReqVO; |
| 7 | +import cn.iocoder.yudao.module.iot.controller.admin.plugin.vo.PluginInfoPageReqVO; | ||
| 8 | +import cn.iocoder.yudao.module.iot.controller.admin.plugin.vo.PluginInfoRespVO; | ||
| 9 | +import cn.iocoder.yudao.module.iot.controller.admin.plugin.vo.PluginInfoSaveReqVO; | ||
| 9 | import cn.iocoder.yudao.module.iot.dal.dataobject.plugininfo.PluginInfoDO; | 10 | import cn.iocoder.yudao.module.iot.dal.dataobject.plugininfo.PluginInfoDO; |
| 10 | -import cn.iocoder.yudao.module.iot.service.plugininfo.PluginInfoService; | 11 | +import cn.iocoder.yudao.module.iot.service.plugin.PluginInfoService; |
| 11 | import io.swagger.v3.oas.annotations.Operation; | 12 | import io.swagger.v3.oas.annotations.Operation; |
| 12 | import io.swagger.v3.oas.annotations.Parameter; | 13 | import io.swagger.v3.oas.annotations.Parameter; |
| 13 | import io.swagger.v3.oas.annotations.tags.Tag; | 14 | import io.swagger.v3.oas.annotations.tags.Tag; |
| @@ -16,11 +17,8 @@ import jakarta.validation.Valid; | @@ -16,11 +17,8 @@ import jakarta.validation.Valid; | ||
| 16 | import org.springframework.security.access.prepost.PreAuthorize; | 17 | import org.springframework.security.access.prepost.PreAuthorize; |
| 17 | import org.springframework.validation.annotation.Validated; | 18 | import org.springframework.validation.annotation.Validated; |
| 18 | import org.springframework.web.bind.annotation.*; | 19 | import org.springframework.web.bind.annotation.*; |
| 19 | -import org.springframework.web.multipart.MultipartFile; | ||
| 20 | 20 | ||
| 21 | -import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception; | ||
| 22 | import static cn.iocoder.yudao.framework.common.pojo.CommonResult.success; | 21 | import static cn.iocoder.yudao.framework.common.pojo.CommonResult.success; |
| 23 | -import static cn.iocoder.yudao.module.infra.enums.ErrorCodeConstants.FILE_IS_EMPTY; | ||
| 24 | 22 | ||
| 25 | @Tag(name = "管理后台 - IoT 插件信息") | 23 | @Tag(name = "管理后台 - IoT 插件信息") |
| 26 | @RestController | 24 | @RestController |
| @@ -72,16 +70,11 @@ public class PluginInfoController { | @@ -72,16 +70,11 @@ public class PluginInfoController { | ||
| 72 | return success(BeanUtils.toBean(pageResult, PluginInfoRespVO.class)); | 70 | return success(BeanUtils.toBean(pageResult, PluginInfoRespVO.class)); |
| 73 | } | 71 | } |
| 74 | 72 | ||
| 75 | - @RequestMapping(value = "/update-jar", | ||
| 76 | - method = {RequestMethod.POST, RequestMethod.PUT}) // 解决 uni-app 不支持 Put 上传文件的问题 | ||
| 77 | - @Operation(summary = "上传Jar包") | ||
| 78 | - public CommonResult<Boolean> uploadJar( | ||
| 79 | - @RequestParam("id") Long id, | ||
| 80 | - @RequestParam("jar") MultipartFile file) throws Exception { | ||
| 81 | - if (file.isEmpty()) { | ||
| 82 | - throw exception(FILE_IS_EMPTY); | ||
| 83 | - } | ||
| 84 | - pluginInfoService.uploadJar(id, file); | 73 | + @PostMapping("/upload-file") |
| 74 | + @Operation(summary = "上传插件文件") | ||
| 75 | + @PreAuthorize("@ss.hasPermission('iot:plugin-info:update')") | ||
| 76 | + public CommonResult<Boolean> uploadFile(@Valid PluginInfoImportReqVO reqVO) { | ||
| 77 | + pluginInfoService.uploadFile(reqVO.getId(), reqVO.getFile()); | ||
| 85 | return success(true); | 78 | return success(true); |
| 86 | } | 79 | } |
| 87 | 80 |
yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/plugin/vo/PluginInfoImportReqVO.java
0 → 100644
| 1 | +package cn.iocoder.yudao.module.iot.controller.admin.plugin.vo; | ||
| 2 | + | ||
| 3 | +import io.swagger.v3.oas.annotations.media.Schema; | ||
| 4 | +import jakarta.validation.constraints.NotNull; | ||
| 5 | +import lombok.Data; | ||
| 6 | +import org.springframework.web.multipart.MultipartFile; | ||
| 7 | + | ||
| 8 | +@Schema(description = "管理后台 - IoT 插件上传 Request VO") | ||
| 9 | +@Data | ||
| 10 | +public class PluginInfoImportReqVO { | ||
| 11 | + | ||
| 12 | + @Schema(description = "主键ID", requiredMode = Schema.RequiredMode.REQUIRED, example = "11546") | ||
| 13 | + private Long id; | ||
| 14 | + | ||
| 15 | + @Schema(description = "插件文件", requiredMode = Schema.RequiredMode.REQUIRED) | ||
| 16 | + @NotNull(message = "插件文件不能为空") | ||
| 17 | + private MultipartFile file; | ||
| 18 | + | ||
| 19 | +} | ||
| 0 | \ No newline at end of file | 20 | \ No newline at end of file |
yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/plugin/vo/PluginInfoPageReqVO.java
0 → 100644
| 1 | +package cn.iocoder.yudao.module.iot.controller.admin.plugin.vo; | ||
| 2 | + | ||
| 3 | +import cn.iocoder.yudao.framework.common.pojo.PageParam; | ||
| 4 | +import io.swagger.v3.oas.annotations.media.Schema; | ||
| 5 | +import lombok.Data; | ||
| 6 | + | ||
| 7 | +@Schema(description = "管理后台 - IoT 插件信息分页 Request VO") | ||
| 8 | +@Data | ||
| 9 | +public class PluginInfoPageReqVO extends PageParam { | ||
| 10 | + | ||
| 11 | + @Schema(description = "插件名称", example = "http") | ||
| 12 | + private String name; | ||
| 13 | + | ||
| 14 | + @Schema(description = "状态") | ||
| 15 | + private Integer status; | ||
| 16 | + | ||
| 17 | +} | ||
| 0 | \ No newline at end of file | 18 | \ No newline at end of file |
yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/plugininfo/vo/PluginInfoRespVO.java renamed to yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/plugin/vo/PluginInfoRespVO.java
| 1 | -package cn.iocoder.yudao.module.iot.controller.admin.plugininfo.vo; | 1 | +package cn.iocoder.yudao.module.iot.controller.admin.plugin.vo; |
| 2 | 2 | ||
| 3 | import com.alibaba.excel.annotation.ExcelIgnoreUnannotated; | 3 | import com.alibaba.excel.annotation.ExcelIgnoreUnannotated; |
| 4 | import com.alibaba.excel.annotation.ExcelProperty; | 4 | import com.alibaba.excel.annotation.ExcelProperty; |
| @@ -16,9 +16,9 @@ public class PluginInfoRespVO { | @@ -16,9 +16,9 @@ public class PluginInfoRespVO { | ||
| 16 | @ExcelProperty("主键 ID") | 16 | @ExcelProperty("主键 ID") |
| 17 | private Long id; | 17 | private Long id; |
| 18 | 18 | ||
| 19 | - @Schema(description = "插件包 ID", requiredMode = Schema.RequiredMode.REQUIRED, example = "24627") | ||
| 20 | - @ExcelProperty("插件包 ID") | ||
| 21 | - private String pluginId; | 19 | + @Schema(description = "插件包标识符", requiredMode = Schema.RequiredMode.REQUIRED, example = "24627") |
| 20 | + @ExcelProperty("插件包标识符") | ||
| 21 | + private String pluginKey; | ||
| 22 | 22 | ||
| 23 | @Schema(description = "插件名称", requiredMode = Schema.RequiredMode.REQUIRED, example = "赵六") | 23 | @Schema(description = "插件名称", requiredMode = Schema.RequiredMode.REQUIRED, example = "赵六") |
| 24 | @ExcelProperty("插件名称") | 24 | @ExcelProperty("插件名称") |
| @@ -34,7 +34,7 @@ public class PluginInfoRespVO { | @@ -34,7 +34,7 @@ public class PluginInfoRespVO { | ||
| 34 | 34 | ||
| 35 | @Schema(description = "插件包文件名", requiredMode = Schema.RequiredMode.REQUIRED) | 35 | @Schema(description = "插件包文件名", requiredMode = Schema.RequiredMode.REQUIRED) |
| 36 | @ExcelProperty("插件包文件名") | 36 | @ExcelProperty("插件包文件名") |
| 37 | - private String file; | 37 | + private String fileName; |
| 38 | 38 | ||
| 39 | @Schema(description = "插件版本", requiredMode = Schema.RequiredMode.REQUIRED) | 39 | @Schema(description = "插件版本", requiredMode = Schema.RequiredMode.REQUIRED) |
| 40 | @ExcelProperty("插件版本") | 40 | @ExcelProperty("插件版本") |
yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/plugininfo/vo/PluginInfoSaveReqVO.java renamed to yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/plugin/vo/PluginInfoSaveReqVO.java
| 1 | -package cn.iocoder.yudao.module.iot.controller.admin.plugininfo.vo; | 1 | +package cn.iocoder.yudao.module.iot.controller.admin.plugin.vo; |
| 2 | 2 | ||
| 3 | import io.swagger.v3.oas.annotations.media.Schema; | 3 | import io.swagger.v3.oas.annotations.media.Schema; |
| 4 | import lombok.*; | 4 | import lombok.*; |
| @@ -10,8 +10,8 @@ public class PluginInfoSaveReqVO { | @@ -10,8 +10,8 @@ public class PluginInfoSaveReqVO { | ||
| 10 | @Schema(description = "主键ID", requiredMode = Schema.RequiredMode.REQUIRED, example = "11546") | 10 | @Schema(description = "主键ID", requiredMode = Schema.RequiredMode.REQUIRED, example = "11546") |
| 11 | private Long id; | 11 | private Long id; |
| 12 | 12 | ||
| 13 | - @Schema(description = "插件包id", requiredMode = Schema.RequiredMode.REQUIRED, example = "24627") | ||
| 14 | - private String pluginId; | 13 | + @Schema(description = "插件包标识符", requiredMode = Schema.RequiredMode.REQUIRED, example = "24627") |
| 14 | + private String pluginKey; | ||
| 15 | 15 | ||
| 16 | @Schema(description = "插件名称", requiredMode = Schema.RequiredMode.REQUIRED, example = "赵六") | 16 | @Schema(description = "插件名称", requiredMode = Schema.RequiredMode.REQUIRED, example = "赵六") |
| 17 | private String name; | 17 | private String name; |
| @@ -23,7 +23,7 @@ public class PluginInfoSaveReqVO { | @@ -23,7 +23,7 @@ public class PluginInfoSaveReqVO { | ||
| 23 | private Integer deployType; | 23 | private Integer deployType; |
| 24 | 24 | ||
| 25 | @Schema(description = "插件包文件名", requiredMode = Schema.RequiredMode.REQUIRED) | 25 | @Schema(description = "插件包文件名", requiredMode = Schema.RequiredMode.REQUIRED) |
| 26 | - private String file; | 26 | + private String fileName; |
| 27 | 27 | ||
| 28 | @Schema(description = "插件版本", requiredMode = Schema.RequiredMode.REQUIRED) | 28 | @Schema(description = "插件版本", requiredMode = Schema.RequiredMode.REQUIRED) |
| 29 | private String version; | 29 | private String version; |
yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/plugininstance/vo/PluginInstancePageReqVO.java renamed to yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/plugin/vo/PluginInstancePageReqVO.java
yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/plugininstance/vo/PluginInstanceRespVO.java renamed to yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/plugin/vo/PluginInstanceRespVO.java
yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/plugininstance/vo/PluginInstanceSaveReqVO.java renamed to yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/plugin/vo/PluginInstanceSaveReqVO.java
yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/plugininfo/PluginController.java deleted
| 1 | -package cn.iocoder.yudao.module.iot.controller.admin.plugininfo; | ||
| 2 | - | ||
| 3 | -import jakarta.annotation.Resource; | ||
| 4 | -import org.pf4j.spring.SpringPluginManager; | ||
| 5 | -import org.springframework.beans.factory.annotation.Value; | ||
| 6 | -import org.springframework.http.HttpStatus; | ||
| 7 | -import org.springframework.http.ResponseEntity; | ||
| 8 | -import org.springframework.web.bind.annotation.*; | ||
| 9 | -import org.springframework.web.multipart.MultipartFile; | ||
| 10 | - | ||
| 11 | -import javax.annotation.security.PermitAll; | ||
| 12 | -import java.io.IOException; | ||
| 13 | -import java.nio.file.Files; | ||
| 14 | -import java.nio.file.Path; | ||
| 15 | -import java.nio.file.Paths; | ||
| 16 | -import java.nio.file.StandardCopyOption; | ||
| 17 | -import java.util.List; | ||
| 18 | -import java.util.stream.Collectors; | ||
| 19 | - | ||
| 20 | -/** | ||
| 21 | - * 插件 Controller 测试用例 | ||
| 22 | - */ | ||
| 23 | -@RestController | ||
| 24 | -@RequestMapping("/iot/plugins") | ||
| 25 | -public class PluginController { | ||
| 26 | - | ||
| 27 | - @Resource | ||
| 28 | - private SpringPluginManager springPluginManager; | ||
| 29 | - | ||
| 30 | - @Value("${pf4j.pluginsDir}") | ||
| 31 | - private String pluginsDir; | ||
| 32 | - | ||
| 33 | - /** | ||
| 34 | - * 上传插件 JAR 文件并加载插件 | ||
| 35 | - * | ||
| 36 | - * @param file 上传的 JAR 文件 | ||
| 37 | - * @return 上传结果 | ||
| 38 | - */ | ||
| 39 | - @PermitAll | ||
| 40 | - @PostMapping("/upload") | ||
| 41 | - public ResponseEntity<String> uploadPlugin(@RequestParam("file") MultipartFile file) { | ||
| 42 | - if (file.isEmpty()) { | ||
| 43 | - return ResponseEntity.status(HttpStatus.BAD_REQUEST).body("上传的文件为空"); | ||
| 44 | - } | ||
| 45 | - | ||
| 46 | - // 确保插件目录存在 | ||
| 47 | - Path pluginsPath = Paths.get(pluginsDir); | ||
| 48 | - try { | ||
| 49 | - if (!Files.exists(pluginsPath)) { | ||
| 50 | - Files.createDirectories(pluginsPath); | ||
| 51 | - } | ||
| 52 | - | ||
| 53 | - // 保存上传的 JAR 文件到插件目录 | ||
| 54 | - String filename = file.getOriginalFilename(); | ||
| 55 | - if (filename == null || (!filename.endsWith(".jar") && !filename.endsWith(".zip"))) { | ||
| 56 | - return ResponseEntity.status(HttpStatus.BAD_REQUEST).body("上传的文件不是 JAR 或 ZIP 文件"); | ||
| 57 | - } | ||
| 58 | - | ||
| 59 | - Path jarPath = pluginsPath.resolve(filename); | ||
| 60 | - | ||
| 61 | - Files.copy(file.getInputStream(), jarPath, StandardCopyOption.REPLACE_EXISTING); | ||
| 62 | - | ||
| 63 | - // 加载插件 | ||
| 64 | - String pluginId = springPluginManager.loadPlugin(jarPath.toAbsolutePath()); | ||
| 65 | - | ||
| 66 | - // 启动插件 | ||
| 67 | - springPluginManager.startPlugin(pluginId); | ||
| 68 | - | ||
| 69 | - return ResponseEntity.ok("插件上传并加载成功"); | ||
| 70 | - } catch (IOException e) { | ||
| 71 | - return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR).body("上传插件时发生错误: " + e.getMessage()); | ||
| 72 | - } catch (Exception e) { | ||
| 73 | - return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR).body("加载插件时发生错误: " + e.getMessage()); | ||
| 74 | - } | ||
| 75 | - } | ||
| 76 | - | ||
| 77 | - /** | ||
| 78 | - * 卸载指定插件 | ||
| 79 | - * | ||
| 80 | - * @param pluginId 插件 ID | ||
| 81 | - * @return 卸载结果 | ||
| 82 | - */ | ||
| 83 | - @PermitAll | ||
| 84 | - @DeleteMapping("/unload/{pluginId}") | ||
| 85 | - public ResponseEntity<String> unloadPlugin(@PathVariable String pluginId) { | ||
| 86 | - if (springPluginManager.getPlugins().stream().noneMatch(plugin -> plugin.getDescriptor().getPluginId().equals(pluginId))) { | ||
| 87 | - return ResponseEntity.status(HttpStatus.BAD_REQUEST).body("插件未加载: " + pluginId); | ||
| 88 | - } | ||
| 89 | - | ||
| 90 | - springPluginManager.stopPlugin(pluginId); | ||
| 91 | - springPluginManager.unloadPlugin(pluginId); | ||
| 92 | - | ||
| 93 | - // 删除插件 JAR 文件(可选) | ||
| 94 | -// PluginWrapper plugin = pluginManager.getPlugin(pluginId); | ||
| 95 | -// PluginDescriptor descriptor = plugin.getDescriptor(); | ||
| 96 | -// Path jarPath = Paths.get(pluginsDir).resolve(descriptor.getPluginId() + ".jar"); | ||
| 97 | -// Files.deleteIfExists(jarPath); | ||
| 98 | - | ||
| 99 | - return ResponseEntity.ok("插件卸载成功: " + pluginId); | ||
| 100 | - } | ||
| 101 | - | ||
| 102 | - /** | ||
| 103 | - * 列出所有已加载的插件 | ||
| 104 | - * | ||
| 105 | - * @return 插件列表 | ||
| 106 | - */ | ||
| 107 | - @PermitAll | ||
| 108 | - @GetMapping("/list") | ||
| 109 | - public ResponseEntity<List<String>> listPlugins() { | ||
| 110 | - List<String> plugins = springPluginManager.getPlugins().stream() | ||
| 111 | - .map(plugin -> plugin.getDescriptor().getPluginId()) | ||
| 112 | - .collect(Collectors.toList()); | ||
| 113 | - return ResponseEntity.ok(plugins); | ||
| 114 | - } | ||
| 115 | - | ||
| 116 | -} | ||
| 117 | \ No newline at end of file | 0 | \ No newline at end of file |
yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/plugininfo/vo/PluginInfoPageReqVO.java deleted
| 1 | -package cn.iocoder.yudao.module.iot.controller.admin.plugininfo.vo; | ||
| 2 | - | ||
| 3 | -import cn.iocoder.yudao.framework.common.validation.InEnum; | ||
| 4 | -import cn.iocoder.yudao.module.iot.enums.plugin.IotPluginTypeEnum; | ||
| 5 | -import lombok.*; | ||
| 6 | -import io.swagger.v3.oas.annotations.media.Schema; | ||
| 7 | -import cn.iocoder.yudao.framework.common.pojo.PageParam; | ||
| 8 | -import org.springframework.format.annotation.DateTimeFormat; | ||
| 9 | -import java.time.LocalDateTime; | ||
| 10 | - | ||
| 11 | -import static cn.iocoder.yudao.framework.common.util.date.DateUtils.FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND; | ||
| 12 | - | ||
| 13 | -// TODO @haohao:只查询必要字段哈 | ||
| 14 | -@Schema(description = "管理后台 - IoT 插件信息分页 Request VO") | ||
| 15 | -@Data | ||
| 16 | -public class PluginInfoPageReqVO extends PageParam { | ||
| 17 | - | ||
| 18 | - @Schema(description = "插件包 ID ", example = "24627") | ||
| 19 | - private String pluginId; | ||
| 20 | - | ||
| 21 | - @Schema(description = "插件名称", example = "赵六") | ||
| 22 | - private String name; | ||
| 23 | - | ||
| 24 | - @Schema(description = "描述", example = "你猜") | ||
| 25 | - private String description; | ||
| 26 | - | ||
| 27 | - @Schema(description = "部署方式", example = "2") | ||
| 28 | - private Integer deployType; | ||
| 29 | - | ||
| 30 | - @Schema(description = "插件包文件名") | ||
| 31 | - private String file; | ||
| 32 | - | ||
| 33 | - @Schema(description = "插件版本") | ||
| 34 | - private String version; | ||
| 35 | - | ||
| 36 | - @Schema(description = "插件类型", example = "2") | ||
| 37 | - @InEnum(IotPluginTypeEnum.class) | ||
| 38 | - private Integer type; | ||
| 39 | - | ||
| 40 | - @Schema(description = "设备插件协议类型") | ||
| 41 | - private String protocol; | ||
| 42 | - | ||
| 43 | - @Schema(description = "状态") | ||
| 44 | - private Integer status; | ||
| 45 | - | ||
| 46 | - @Schema(description = "插件配置项描述信息") | ||
| 47 | - private String configSchema; | ||
| 48 | - | ||
| 49 | - @Schema(description = "插件配置信息") | ||
| 50 | - private String config; | ||
| 51 | - | ||
| 52 | - @Schema(description = "插件脚本") | ||
| 53 | - private String script; | ||
| 54 | - | ||
| 55 | - @Schema(description = "创建时间") | ||
| 56 | - @DateTimeFormat(pattern = FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND) | ||
| 57 | - private LocalDateTime[] createTime; | ||
| 58 | - | ||
| 59 | -} | ||
| 60 | \ No newline at end of file | 0 | \ No newline at end of file |
yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/plugininstance/PluginInstanceController.java deleted
| 1 | -package cn.iocoder.yudao.module.iot.controller.admin.plugininstance; | ||
| 2 | - | ||
| 3 | -import org.springframework.web.bind.annotation.*; | ||
| 4 | -import jakarta.annotation.Resource; | ||
| 5 | -import org.springframework.validation.annotation.Validated; | ||
| 6 | -import org.springframework.security.access.prepost.PreAuthorize; | ||
| 7 | -import io.swagger.v3.oas.annotations.tags.Tag; | ||
| 8 | -import io.swagger.v3.oas.annotations.Parameter; | ||
| 9 | -import io.swagger.v3.oas.annotations.Operation; | ||
| 10 | - | ||
| 11 | -import jakarta.validation.*; | ||
| 12 | -import jakarta.servlet.http.*; | ||
| 13 | -import java.util.*; | ||
| 14 | -import java.io.IOException; | ||
| 15 | - | ||
| 16 | -import cn.iocoder.yudao.framework.common.pojo.PageParam; | ||
| 17 | -import cn.iocoder.yudao.framework.common.pojo.PageResult; | ||
| 18 | -import cn.iocoder.yudao.framework.common.pojo.CommonResult; | ||
| 19 | -import cn.iocoder.yudao.framework.common.util.object.BeanUtils; | ||
| 20 | -import static cn.iocoder.yudao.framework.common.pojo.CommonResult.success; | ||
| 21 | - | ||
| 22 | -import cn.iocoder.yudao.framework.excel.core.util.ExcelUtils; | ||
| 23 | - | ||
| 24 | -import cn.iocoder.yudao.framework.apilog.core.annotation.ApiAccessLog; | ||
| 25 | -import static cn.iocoder.yudao.framework.apilog.core.enums.OperateTypeEnum.*; | ||
| 26 | - | ||
| 27 | -import cn.iocoder.yudao.module.iot.controller.admin.plugininstance.vo.*; | ||
| 28 | -import cn.iocoder.yudao.module.iot.dal.dataobject.plugininstance.PluginInstanceDO; | ||
| 29 | -import cn.iocoder.yudao.module.iot.service.plugininstance.PluginInstanceService; | ||
| 30 | - | ||
| 31 | -@Tag(name = "管理后台 - IoT 插件实例") | ||
| 32 | -@RestController | ||
| 33 | -@RequestMapping("/iot/plugin-instance") | ||
| 34 | -@Validated | ||
| 35 | -public class PluginInstanceController { | ||
| 36 | - | ||
| 37 | - @Resource | ||
| 38 | - private PluginInstanceService pluginInstanceService; | ||
| 39 | - | ||
| 40 | - @PostMapping("/create") | ||
| 41 | - @Operation(summary = "创建IoT 插件实例") | ||
| 42 | - @PreAuthorize("@ss.hasPermission('iot:plugin-instance:create')") | ||
| 43 | - public CommonResult<Long> createPluginInstance(@Valid @RequestBody PluginInstanceSaveReqVO createReqVO) { | ||
| 44 | - return success(pluginInstanceService.createPluginInstance(createReqVO)); | ||
| 45 | - } | ||
| 46 | - | ||
| 47 | - @PutMapping("/update") | ||
| 48 | - @Operation(summary = "更新IoT 插件实例") | ||
| 49 | - @PreAuthorize("@ss.hasPermission('iot:plugin-instance:update')") | ||
| 50 | - public CommonResult<Boolean> updatePluginInstance(@Valid @RequestBody PluginInstanceSaveReqVO updateReqVO) { | ||
| 51 | - pluginInstanceService.updatePluginInstance(updateReqVO); | ||
| 52 | - return success(true); | ||
| 53 | - } | ||
| 54 | - | ||
| 55 | - @DeleteMapping("/delete") | ||
| 56 | - @Operation(summary = "删除IoT 插件实例") | ||
| 57 | - @Parameter(name = "id", description = "编号", required = true) | ||
| 58 | - @PreAuthorize("@ss.hasPermission('iot:plugin-instance:delete')") | ||
| 59 | - public CommonResult<Boolean> deletePluginInstance(@RequestParam("id") Long id) { | ||
| 60 | - pluginInstanceService.deletePluginInstance(id); | ||
| 61 | - return success(true); | ||
| 62 | - } | ||
| 63 | - | ||
| 64 | - @GetMapping("/get") | ||
| 65 | - @Operation(summary = "获得IoT 插件实例") | ||
| 66 | - @Parameter(name = "id", description = "编号", required = true, example = "1024") | ||
| 67 | - @PreAuthorize("@ss.hasPermission('iot:plugin-instance:query')") | ||
| 68 | - public CommonResult<PluginInstanceRespVO> getPluginInstance(@RequestParam("id") Long id) { | ||
| 69 | - PluginInstanceDO pluginInstance = pluginInstanceService.getPluginInstance(id); | ||
| 70 | - return success(BeanUtils.toBean(pluginInstance, PluginInstanceRespVO.class)); | ||
| 71 | - } | ||
| 72 | - | ||
| 73 | - @GetMapping("/page") | ||
| 74 | - @Operation(summary = "获得IoT 插件实例分页") | ||
| 75 | - @PreAuthorize("@ss.hasPermission('iot:plugin-instance:query')") | ||
| 76 | - public CommonResult<PageResult<PluginInstanceRespVO>> getPluginInstancePage(@Valid PluginInstancePageReqVO pageReqVO) { | ||
| 77 | - PageResult<PluginInstanceDO> pageResult = pluginInstanceService.getPluginInstancePage(pageReqVO); | ||
| 78 | - return success(BeanUtils.toBean(pageResult, PluginInstanceRespVO.class)); | ||
| 79 | - } | ||
| 80 | - | ||
| 81 | - @GetMapping("/export-excel") | ||
| 82 | - @Operation(summary = "导出IoT 插件实例 Excel") | ||
| 83 | - @PreAuthorize("@ss.hasPermission('iot:plugin-instance:export')") | ||
| 84 | - @ApiAccessLog(operateType = EXPORT) | ||
| 85 | - public void exportPluginInstanceExcel(@Valid PluginInstancePageReqVO pageReqVO, | ||
| 86 | - HttpServletResponse response) throws IOException { | ||
| 87 | - pageReqVO.setPageSize(PageParam.PAGE_SIZE_NONE); | ||
| 88 | - List<PluginInstanceDO> list = pluginInstanceService.getPluginInstancePage(pageReqVO).getList(); | ||
| 89 | - // 导出 Excel | ||
| 90 | - ExcelUtils.write(response, "IoT 插件实例.xls", "数据", PluginInstanceRespVO.class, | ||
| 91 | - BeanUtils.toBean(list, PluginInstanceRespVO.class)); | ||
| 92 | - } | ||
| 93 | - | ||
| 94 | -} | ||
| 95 | \ No newline at end of file | 0 | \ No newline at end of file |
yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/thingmodel/IotProductThingModelController.http renamed to 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}} | @@ -25,8 +25,7 @@ Authorization: Bearer {{token}} | ||
| 25 | "defaultValue": "30", | 25 | "defaultValue": "30", |
| 26 | "unit": "%", | 26 | "unit": "%", |
| 27 | "unitName": "百分比" | 27 | "unitName": "百分比" |
| 28 | - }, | ||
| 29 | - "description": "当前温度值" | 28 | + } |
| 30 | } | 29 | } |
| 31 | } | 30 | } |
| 32 | 31 | ||
| @@ -46,7 +45,6 @@ Authorization: Bearer {{token}} | @@ -46,7 +45,6 @@ Authorization: Bearer {{token}} | ||
| 46 | "property": { | 45 | "property": { |
| 47 | "identifier": "switch", | 46 | "identifier": "switch", |
| 48 | "name": "开关", | 47 | "name": "开关", |
| 49 | - "description": "温度计开关", | ||
| 50 | "accessMode": "rw", | 48 | "accessMode": "rw", |
| 51 | "required": true, | 49 | "required": true, |
| 52 | "dataType": "bool", | 50 | "dataType": "bool", |
| @@ -81,7 +79,6 @@ Authorization: Bearer {{token}} | @@ -81,7 +79,6 @@ Authorization: Bearer {{token}} | ||
| 81 | "property": { | 79 | "property": { |
| 82 | "identifier": "argb", | 80 | "identifier": "argb", |
| 83 | "name": "温度计 argb 颜色", | 81 | "name": "温度计 argb 颜色", |
| 84 | - "description": "温度计 argb 颜色", | ||
| 85 | "accessMode": "rw", | 82 | "accessMode": "rw", |
| 86 | "required": true, | 83 | "required": true, |
| 87 | "dataType": "array", | 84 | "dataType": "array", |
| @@ -93,7 +90,6 @@ Authorization: Bearer {{token}} | @@ -93,7 +90,6 @@ Authorization: Bearer {{token}} | ||
| 93 | { | 90 | { |
| 94 | "identifier": "switch", | 91 | "identifier": "switch", |
| 95 | "name": "开关", | 92 | "name": "开关", |
| 96 | - "description": "温度计开关", | ||
| 97 | "accessMode": "rw", | 93 | "accessMode": "rw", |
| 98 | "required": true, | 94 | "required": true, |
| 99 | "dataType": "struct", | 95 | "dataType": "struct", |
| @@ -126,8 +122,7 @@ Authorization: Bearer {{token}} | @@ -126,8 +122,7 @@ Authorization: Bearer {{token}} | ||
| 126 | "defaultValue": "30", | 122 | "defaultValue": "30", |
| 127 | "unit": "%", | 123 | "unit": "%", |
| 128 | "unitName": "百分比" | 124 | "unitName": "百分比" |
| 129 | - }, | ||
| 130 | - "description": "当前温度值" | 125 | + } |
| 131 | } | 126 | } |
| 132 | ] | 127 | ] |
| 133 | } | 128 | } |
| @@ -151,7 +146,6 @@ Authorization: Bearer {{token}} | @@ -151,7 +146,6 @@ Authorization: Bearer {{token}} | ||
| 151 | "property": { | 146 | "property": { |
| 152 | "identifier": "switch", | 147 | "identifier": "switch", |
| 153 | "name": "开关", | 148 | "name": "开关", |
| 154 | - "description": "温度计开关", | ||
| 155 | "accessMode": "r", | 149 | "accessMode": "r", |
| 156 | "required": true, | 150 | "required": true, |
| 157 | "dataType": "bool", | 151 | "dataType": "bool", |
| @@ -176,7 +170,7 @@ tenant-id: {{adminTenentId}} | @@ -176,7 +170,7 @@ tenant-id: {{adminTenentId}} | ||
| 176 | Authorization: Bearer {{token}} | 170 | Authorization: Bearer {{token}} |
| 177 | 171 | ||
| 178 | ### 请求 /iot/product-thing-model/get 接口 => 成功 | 172 | ### 请求 /iot/product-thing-model/get 接口 => 成功 |
| 179 | -GET {{baseUrl}}/iot/product-thing-model/get?id=40 | 173 | +GET {{baseUrl}}/iot/product-thing-model/get?id=67 |
| 180 | tenant-id: {{adminTenentId}} | 174 | tenant-id: {{adminTenentId}} |
| 181 | Authorization: Bearer {{token}} | 175 | Authorization: Bearer {{token}} |
| 182 | 176 |
yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/thingmodel/IotProductThingModelController.java renamed to yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/thingmodel/IotThingModelController.java
| @@ -3,12 +3,12 @@ package cn.iocoder.yudao.module.iot.controller.admin.thingmodel; | @@ -3,12 +3,12 @@ package cn.iocoder.yudao.module.iot.controller.admin.thingmodel; | ||
| 3 | import cn.iocoder.yudao.framework.common.pojo.CommonResult; | 3 | import cn.iocoder.yudao.framework.common.pojo.CommonResult; |
| 4 | import cn.iocoder.yudao.framework.common.pojo.PageResult; | 4 | import cn.iocoder.yudao.framework.common.pojo.PageResult; |
| 5 | import cn.iocoder.yudao.framework.common.util.object.BeanUtils; | 5 | import cn.iocoder.yudao.framework.common.util.object.BeanUtils; |
| 6 | -import cn.iocoder.yudao.module.iot.controller.admin.thingmodel.vo.IotProductThingModelPageReqVO; | ||
| 7 | -import cn.iocoder.yudao.module.iot.controller.admin.thingmodel.vo.IotProductThingModelRespVO; | ||
| 8 | -import cn.iocoder.yudao.module.iot.controller.admin.thingmodel.vo.IotProductThingModelSaveReqVO; | ||
| 9 | -import cn.iocoder.yudao.module.iot.convert.thingmodel.IotProductThingModelConvert; | ||
| 10 | -import cn.iocoder.yudao.module.iot.dal.dataobject.thingmodel.IotProductThingModelDO; | ||
| 11 | -import cn.iocoder.yudao.module.iot.service.thingmodel.IotProductThingModelService; | 6 | +import cn.iocoder.yudao.module.iot.controller.admin.thingmodel.vo.IotThingModelPageReqVO; |
| 7 | +import cn.iocoder.yudao.module.iot.controller.admin.thingmodel.vo.IotThingModelRespVO; | ||
| 8 | +import cn.iocoder.yudao.module.iot.controller.admin.thingmodel.vo.IotThingModelSaveReqVO; | ||
| 9 | +import cn.iocoder.yudao.module.iot.convert.thingmodel.IotThingModelConvert; | ||
| 10 | +import cn.iocoder.yudao.module.iot.dal.dataobject.thingmodel.IotThingModelDO; | ||
| 11 | +import cn.iocoder.yudao.module.iot.service.thingmodel.IotThingModelService; | ||
| 12 | import io.swagger.v3.oas.annotations.Operation; | 12 | import io.swagger.v3.oas.annotations.Operation; |
| 13 | import io.swagger.v3.oas.annotations.Parameter; | 13 | import io.swagger.v3.oas.annotations.Parameter; |
| 14 | import io.swagger.v3.oas.annotations.tags.Tag; | 14 | import io.swagger.v3.oas.annotations.tags.Tag; |
| @@ -24,61 +24,61 @@ import static cn.iocoder.yudao.framework.common.pojo.CommonResult.success; | @@ -24,61 +24,61 @@ import static cn.iocoder.yudao.framework.common.pojo.CommonResult.success; | ||
| 24 | 24 | ||
| 25 | @Tag(name = "管理后台 - IoT 产品物模型") | 25 | @Tag(name = "管理后台 - IoT 产品物模型") |
| 26 | @RestController | 26 | @RestController |
| 27 | -@RequestMapping("/iot/product-thing-model") | 27 | +@RequestMapping("/iot/thing-model") |
| 28 | @Validated | 28 | @Validated |
| 29 | -public class IotProductThingModelController { | 29 | +public class IotThingModelController { |
| 30 | 30 | ||
| 31 | @Resource | 31 | @Resource |
| 32 | - private IotProductThingModelService thingModelService; | 32 | + private IotThingModelService thingModelService; |
| 33 | 33 | ||
| 34 | @PostMapping("/create") | 34 | @PostMapping("/create") |
| 35 | @Operation(summary = "创建产品物模型") | 35 | @Operation(summary = "创建产品物模型") |
| 36 | - @PreAuthorize("@ss.hasPermission('iot:product-thing-model:create')") | ||
| 37 | - public CommonResult<Long> createProductThingModel(@Valid @RequestBody IotProductThingModelSaveReqVO createReqVO) { | ||
| 38 | - return success(thingModelService.createProductThingModel(createReqVO)); | 36 | + @PreAuthorize("@ss.hasPermission('iot:thing-model:create')") |
| 37 | + public CommonResult<Long> createThingModel(@Valid @RequestBody IotThingModelSaveReqVO createReqVO) { | ||
| 38 | + return success(thingModelService.createThingModel(createReqVO)); | ||
| 39 | } | 39 | } |
| 40 | 40 | ||
| 41 | @PutMapping("/update") | 41 | @PutMapping("/update") |
| 42 | @Operation(summary = "更新产品物模型") | 42 | @Operation(summary = "更新产品物模型") |
| 43 | - @PreAuthorize("@ss.hasPermission('iot:product-thing-model:update')") | ||
| 44 | - public CommonResult<Boolean> updateProductThingModel(@Valid @RequestBody IotProductThingModelSaveReqVO updateReqVO) { | ||
| 45 | - thingModelService.updateProductThingModel(updateReqVO); | 43 | + @PreAuthorize("@ss.hasPermission('iot:thing-model:update')") |
| 44 | + public CommonResult<Boolean> updateThingModel(@Valid @RequestBody IotThingModelSaveReqVO updateReqVO) { | ||
| 45 | + thingModelService.updateThingModel(updateReqVO); | ||
| 46 | return success(true); | 46 | return success(true); |
| 47 | } | 47 | } |
| 48 | 48 | ||
| 49 | @DeleteMapping("/delete") | 49 | @DeleteMapping("/delete") |
| 50 | @Operation(summary = "删除产品物模型") | 50 | @Operation(summary = "删除产品物模型") |
| 51 | @Parameter(name = "id", description = "编号", required = true) | 51 | @Parameter(name = "id", description = "编号", required = true) |
| 52 | - @PreAuthorize("@ss.hasPermission('iot:product-thing-model:delete')") | ||
| 53 | - public CommonResult<Boolean> deleteProductThingModel(@RequestParam("id") Long id) { | ||
| 54 | - thingModelService.deleteProductThingModel(id); | 52 | + @PreAuthorize("@ss.hasPermission('iot:thing-model:delete')") |
| 53 | + public CommonResult<Boolean> deleteThingModel(@RequestParam("id") Long id) { | ||
| 54 | + thingModelService.deleteThingModel(id); | ||
| 55 | return success(true); | 55 | return success(true); |
| 56 | } | 56 | } |
| 57 | 57 | ||
| 58 | @GetMapping("/get") | 58 | @GetMapping("/get") |
| 59 | @Operation(summary = "获得产品物模型") | 59 | @Operation(summary = "获得产品物模型") |
| 60 | @Parameter(name = "id", description = "编号", required = true) | 60 | @Parameter(name = "id", description = "编号", required = true) |
| 61 | - @PreAuthorize("@ss.hasPermission('iot:product-thing-model:query')") | ||
| 62 | - public CommonResult<IotProductThingModelRespVO> getProductThingModel(@RequestParam("id") Long id) { | ||
| 63 | - IotProductThingModelDO thingModel = thingModelService.getProductThingModel(id); | ||
| 64 | - return success(IotProductThingModelConvert.INSTANCE.convert(thingModel)); | 61 | + @PreAuthorize("@ss.hasPermission('iot:thing-model:query')") |
| 62 | + public CommonResult<IotThingModelRespVO> getThingModel(@RequestParam("id") Long id) { | ||
| 63 | + IotThingModelDO thingModel = thingModelService.getThingModel(id); | ||
| 64 | + return success(IotThingModelConvert.INSTANCE.convert(thingModel)); | ||
| 65 | } | 65 | } |
| 66 | 66 | ||
| 67 | @GetMapping("/list-by-product-id") | 67 | @GetMapping("/list-by-product-id") |
| 68 | @Operation(summary = "获得产品物模型") | 68 | @Operation(summary = "获得产品物模型") |
| 69 | @Parameter(name = "productId", description = "产品ID", required = true, example = "1024") | 69 | @Parameter(name = "productId", description = "产品ID", required = true, example = "1024") |
| 70 | - @PreAuthorize("@ss.hasPermission('iot:product-thing-model:query')") | ||
| 71 | - public CommonResult<List<IotProductThingModelRespVO>> getProductThingModelListByProductId(@RequestParam("productId") Long productId) { | ||
| 72 | - List<IotProductThingModelDO> list = thingModelService.getProductThingModelListByProductId(productId); | ||
| 73 | - return success(IotProductThingModelConvert.INSTANCE.convertList(list)); | 70 | + @PreAuthorize("@ss.hasPermission('iot:thing-model:query')") |
| 71 | + public CommonResult<List<IotThingModelRespVO>> getThingModelListByProductId(@RequestParam("productId") Long productId) { | ||
| 72 | + List<IotThingModelDO> list = thingModelService.getThingModelListByProductId(productId); | ||
| 73 | + return success(IotThingModelConvert.INSTANCE.convertList(list)); | ||
| 74 | } | 74 | } |
| 75 | 75 | ||
| 76 | @GetMapping("/page") | 76 | @GetMapping("/page") |
| 77 | @Operation(summary = "获得产品物模型分页") | 77 | @Operation(summary = "获得产品物模型分页") |
| 78 | - @PreAuthorize("@ss.hasPermission('iot:product-thing-model:query')") | ||
| 79 | - public CommonResult<PageResult<IotProductThingModelRespVO>> getProductThingModelPage(@Valid IotProductThingModelPageReqVO pageReqVO) { | ||
| 80 | - PageResult<IotProductThingModelDO> pageResult = thingModelService.getProductThingModelPage(pageReqVO); | ||
| 81 | - return success(BeanUtils.toBean(pageResult, IotProductThingModelRespVO.class)); | 78 | + @PreAuthorize("@ss.hasPermission('iot:thing-model:query')") |
| 79 | + public CommonResult<PageResult<IotThingModelRespVO>> getThingModelPage(@Valid IotThingModelPageReqVO pageReqVO) { | ||
| 80 | + PageResult<IotThingModelDO> pageResult = thingModelService.getProductThingModelPage(pageReqVO); | ||
| 81 | + return success(BeanUtils.toBean(pageResult, IotThingModelRespVO.class)); | ||
| 82 | } | 82 | } |
| 83 | 83 | ||
| 84 | } | 84 | } |
yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/thingmodel/model/ThingModelEvent.java
| 1 | package cn.iocoder.yudao.module.iot.controller.admin.thingmodel.model; | 1 | package cn.iocoder.yudao.module.iot.controller.admin.thingmodel.model; |
| 2 | 2 | ||
| 3 | -import cn.iocoder.yudao.module.iot.controller.admin.thingmodel.model.dataType.ThingModelArgument; | 3 | +import cn.iocoder.yudao.module.iot.enums.thingmodel.IotThingModelServiceEventTypeEnum; |
| 4 | import lombok.Data; | 4 | import lombok.Data; |
| 5 | + | ||
| 5 | import java.util.List; | 6 | import java.util.List; |
| 6 | 7 | ||
| 8 | +/** | ||
| 9 | + * 物模型中的事件 | ||
| 10 | + * | ||
| 11 | + * @author HUIHUI | ||
| 12 | + */ | ||
| 7 | @Data | 13 | @Data |
| 8 | public class ThingModelEvent { | 14 | public class ThingModelEvent { |
| 9 | 15 | ||
| @@ -16,17 +22,24 @@ public class ThingModelEvent { | @@ -16,17 +22,24 @@ public class ThingModelEvent { | ||
| 16 | */ | 22 | */ |
| 17 | private String name; | 23 | private String name; |
| 18 | /** | 24 | /** |
| 19 | - * 事件描述 | 25 | + * 是否是标准品类的必选事件 |
| 20 | */ | 26 | */ |
| 21 | - private String description; | ||
| 22 | - | 27 | + private Boolean required; |
| 23 | /** | 28 | /** |
| 24 | * 事件类型 | 29 | * 事件类型 |
| 25 | * | 30 | * |
| 26 | - * "info"、"alert"、"error" | 31 | + * 枚举 {@link IotThingModelServiceEventTypeEnum} |
| 27 | */ | 32 | */ |
| 28 | private String type; | 33 | private String type; |
| 29 | - private List<ThingModelArgument> outputData; | 34 | + /** |
| 35 | + * 事件的输出参数 | ||
| 36 | + * | ||
| 37 | + * 输出参数定义事件调用后返回的结果或反馈信息,用于确认操作结果或提供额外的信息。 | ||
| 38 | + */ | ||
| 39 | + private List<ThingModelParam> outputParams; | ||
| 40 | + /** | ||
| 41 | + * 标识设备需要执行的具体操作 | ||
| 42 | + */ | ||
| 30 | private String method; | 43 | private String method; |
| 31 | 44 | ||
| 32 | } | 45 | } |
yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/thingmodel/model/ThingModelParam.java
0 → 100644
| 1 | +package cn.iocoder.yudao.module.iot.controller.admin.thingmodel.model; | ||
| 2 | + | ||
| 3 | +import cn.iocoder.yudao.module.iot.controller.admin.thingmodel.model.dataType.ThingModelDataSpecs; | ||
| 4 | +import cn.iocoder.yudao.module.iot.enums.thingmodel.IotThingModelParamDirectionEnum; | ||
| 5 | +import lombok.Data; | ||
| 6 | + | ||
| 7 | +import java.util.List; | ||
| 8 | + | ||
| 9 | +/** | ||
| 10 | + * IOT 产品物模型中的参数 | ||
| 11 | + * | ||
| 12 | + * @author HUIHUI | ||
| 13 | + */ | ||
| 14 | +@Data | ||
| 15 | +public class ThingModelParam { | ||
| 16 | + | ||
| 17 | + /** | ||
| 18 | + * 参数标识符 | ||
| 19 | + */ | ||
| 20 | + private String identifier; | ||
| 21 | + /** | ||
| 22 | + * 参数名称 | ||
| 23 | + */ | ||
| 24 | + private String name; | ||
| 25 | + /** | ||
| 26 | + * 用于区分输入或输出参数 | ||
| 27 | + * | ||
| 28 | + * 枚举 {@link IotThingModelParamDirectionEnum} | ||
| 29 | + */ | ||
| 30 | + private String direction; | ||
| 31 | + /** | ||
| 32 | + * 参数的序号。从 0 开始排序,且不能重复。 | ||
| 33 | + * | ||
| 34 | + * TODO 考虑要不要序号,感觉是要的, 先留一手看看 | ||
| 35 | + */ | ||
| 36 | + private Integer paraOrder; | ||
| 37 | + /** | ||
| 38 | + * 参数值的数据类型,与 dataSpecs 的 dataType 保持一致 | ||
| 39 | + */ | ||
| 40 | + private String dataType; | ||
| 41 | + /** | ||
| 42 | + * 参数值的数据类型(dataType)为非列表型(int、float、double、text、date、array)的数据规范存储在 dataSpecs 中 | ||
| 43 | + */ | ||
| 44 | + private ThingModelDataSpecs dataSpecs; | ||
| 45 | + /** | ||
| 46 | + * 参数值的数据类型(dataType)为列表型(enum、bool、struct)的数据规范存储在 dataSpecsList 中 | ||
| 47 | + */ | ||
| 48 | + private List<ThingModelDataSpecs> dataSpecsList; | ||
| 49 | + | ||
| 50 | +} |
yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/thingmodel/model/ThingModelProperty.java
| 1 | package cn.iocoder.yudao.module.iot.controller.admin.thingmodel.model; | 1 | package cn.iocoder.yudao.module.iot.controller.admin.thingmodel.model; |
| 2 | 2 | ||
| 3 | import cn.iocoder.yudao.module.iot.controller.admin.thingmodel.model.dataType.ThingModelDataSpecs; | 3 | import cn.iocoder.yudao.module.iot.controller.admin.thingmodel.model.dataType.ThingModelDataSpecs; |
| 4 | -import cn.iocoder.yudao.module.iot.enums.thingmodel.IotProductThingModelAccessModeEnum; | 4 | +import cn.iocoder.yudao.module.iot.enums.thingmodel.IotThingModelAccessModeEnum; |
| 5 | import lombok.Data; | 5 | import lombok.Data; |
| 6 | 6 | ||
| 7 | import java.util.List; | 7 | import java.util.List; |
| @@ -25,12 +25,9 @@ public class ThingModelProperty { | @@ -25,12 +25,9 @@ public class ThingModelProperty { | ||
| 25 | */ | 25 | */ |
| 26 | private String name; | 26 | private String name; |
| 27 | /** | 27 | /** |
| 28 | - * 属性描述 | ||
| 29 | - */ | ||
| 30 | - private String description; | ||
| 31 | - /** | ||
| 32 | * 云端可以对该属性进行的操作类型 | 28 | * 云端可以对该属性进行的操作类型 |
| 33 | - * 关联枚举 {@link IotProductThingModelAccessModeEnum} | 29 | + * |
| 30 | + * 枚举 {@link IotThingModelAccessModeEnum} | ||
| 34 | */ | 31 | */ |
| 35 | private String accessMode; | 32 | private String accessMode; |
| 36 | /** | 33 | /** |
| @@ -42,6 +39,8 @@ public class ThingModelProperty { | @@ -42,6 +39,8 @@ public class ThingModelProperty { | ||
| 42 | private Boolean required; | 39 | private Boolean required; |
| 43 | /** | 40 | /** |
| 44 | * 数据类型,与 dataSpecs 的 dataType 保持一致 | 41 | * 数据类型,与 dataSpecs 的 dataType 保持一致 |
| 42 | + * | ||
| 43 | + * 枚举 {@link cn.iocoder.yudao.module.iot.enums.thingmodel.IotDataSpecsDataTypeEnum} | ||
| 45 | */ | 44 | */ |
| 46 | private String dataType; | 45 | private String dataType; |
| 47 | /** | 46 | /** |
yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/thingmodel/model/ThingModelService.java
| 1 | package cn.iocoder.yudao.module.iot.controller.admin.thingmodel.model; | 1 | package cn.iocoder.yudao.module.iot.controller.admin.thingmodel.model; |
| 2 | 2 | ||
| 3 | -import cn.iocoder.yudao.module.iot.controller.admin.thingmodel.model.dataType.ThingModelArgument; | 3 | +import cn.iocoder.yudao.module.iot.enums.thingmodel.IotThingModelServiceCallTypeEnum; |
| 4 | import lombok.Data; | 4 | import lombok.Data; |
| 5 | + | ||
| 5 | import java.util.List; | 6 | import java.util.List; |
| 6 | 7 | ||
| 8 | +/** | ||
| 9 | + * 物模型中的服务 | ||
| 10 | + * | ||
| 11 | + * @author HUIHUI | ||
| 12 | + */ | ||
| 7 | @Data | 13 | @Data |
| 8 | public class ThingModelService { | 14 | public class ThingModelService { |
| 9 | 15 | ||
| @@ -16,18 +22,30 @@ public class ThingModelService { | @@ -16,18 +22,30 @@ public class ThingModelService { | ||
| 16 | */ | 22 | */ |
| 17 | private String name; | 23 | private String name; |
| 18 | /** | 24 | /** |
| 19 | - * 服务描述 | 25 | + * 是否是标准品类的必选服务 |
| 20 | */ | 26 | */ |
| 21 | - private String description; | ||
| 22 | - | 27 | + private Boolean required; |
| 23 | /** | 28 | /** |
| 24 | * 调用类型 | 29 | * 调用类型 |
| 25 | * | 30 | * |
| 26 | - * "sync"、"async" | 31 | + * 枚举 {@link IotThingModelServiceCallTypeEnum} |
| 27 | */ | 32 | */ |
| 28 | private String callType; | 33 | private String callType; |
| 29 | - private List<ThingModelArgument> inputData; | ||
| 30 | - private List<ThingModelArgument> outputData; | 34 | + /** |
| 35 | + * 服务的输入参数 | ||
| 36 | + * | ||
| 37 | + * 输入参数定义服务调用时所需提供的信息,用于控制设备行为或执行特定任务 | ||
| 38 | + */ | ||
| 39 | + private List<ThingModelParam> inputParams; | ||
| 40 | + /** | ||
| 41 | + * 服务的输出参数 | ||
| 42 | + * | ||
| 43 | + * 输出参数定义服务调用后返回的结果或反馈信息,用于确认操作结果或提供额外的信息。 | ||
| 44 | + */ | ||
| 45 | + private List<ThingModelParam> outputParams; | ||
| 46 | + /** | ||
| 47 | + * 标识设备需要执行的具体操作 | ||
| 48 | + */ | ||
| 31 | private String method; | 49 | private String method; |
| 32 | 50 | ||
| 33 | } | 51 | } |
yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/thingmodel/model/dataType/ThingModelArgument.java deleted
| 1 | -package cn.iocoder.yudao.module.iot.controller.admin.thingmodel.model.dataType; | ||
| 2 | - | ||
| 3 | -import cn.iocoder.yudao.module.iot.controller.admin.thingmodel.model.ThingModelProperty; | ||
| 4 | -import com.fasterxml.jackson.annotation.JsonIgnoreProperties; | ||
| 5 | -import lombok.Data; | ||
| 6 | - | ||
| 7 | -@Data | ||
| 8 | -@JsonIgnoreProperties(ignoreUnknown = true) | ||
| 9 | -public class ThingModelArgument { | ||
| 10 | - | ||
| 11 | - public static final String DIRECTION_INPUT = "input"; | ||
| 12 | - public static final String DIRECTION_OUTPUT = "output"; | ||
| 13 | - | ||
| 14 | - private String identifier; | ||
| 15 | - private String name; | ||
| 16 | - /** | ||
| 17 | - * 物模型中的属性 | ||
| 18 | - */ | ||
| 19 | - private ThingModelProperty property; | ||
| 20 | - /** | ||
| 21 | - * 用于区分输入或输出参数,"input" 或 "output" | ||
| 22 | - */ | ||
| 23 | - private String direction; | ||
| 24 | - private String description; | ||
| 25 | - | ||
| 26 | -} |
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 { | @@ -20,7 +20,7 @@ public class ThingModelDateOrTextDataSpecs extends ThingModelDataSpecs { | ||
| 20 | * 数据长度,单位为字节。取值不能超过 2048。 | 20 | * 数据长度,单位为字节。取值不能超过 2048。 |
| 21 | * 当 dataType 为 text 时,需传入该参数。 | 21 | * 当 dataType 为 text 时,需传入该参数。 |
| 22 | */ | 22 | */ |
| 23 | - private Long length; | 23 | + private Integer length; |
| 24 | /** | 24 | /** |
| 25 | * 默认值,可选参数,用于存储默认值。 | 25 | * 默认值,可选参数,用于存储默认值。 |
| 26 | */ | 26 | */ |
yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/thingmodel/model/dataType/ThingModelStructDataSpecs.java
| 1 | package cn.iocoder.yudao.module.iot.controller.admin.thingmodel.model.dataType; | 1 | package cn.iocoder.yudao.module.iot.controller.admin.thingmodel.model.dataType; |
| 2 | 2 | ||
| 3 | -import cn.iocoder.yudao.module.iot.enums.thingmodel.IotProductThingModelAccessModeEnum; | 3 | +import cn.iocoder.yudao.module.iot.enums.thingmodel.IotThingModelAccessModeEnum; |
| 4 | import com.fasterxml.jackson.annotation.JsonIgnoreProperties; | 4 | import com.fasterxml.jackson.annotation.JsonIgnoreProperties; |
| 5 | import lombok.Data; | 5 | import lombok.Data; |
| 6 | import lombok.EqualsAndHashCode; | 6 | import lombok.EqualsAndHashCode; |
| @@ -26,19 +26,13 @@ public class ThingModelStructDataSpecs extends ThingModelDataSpecs { | @@ -26,19 +26,13 @@ public class ThingModelStructDataSpecs extends ThingModelDataSpecs { | ||
| 26 | */ | 26 | */ |
| 27 | private String name; | 27 | private String name; |
| 28 | /** | 28 | /** |
| 29 | - * 属性描述 | ||
| 30 | - */ | ||
| 31 | - private String description; | ||
| 32 | - /** | ||
| 33 | * 云端可以对该属性进行的操作类型 | 29 | * 云端可以对该属性进行的操作类型 |
| 34 | - * 关联枚举 {@link IotProductThingModelAccessModeEnum} | 30 | + * |
| 31 | + * 枚举 {@link IotThingModelAccessModeEnum} | ||
| 35 | */ | 32 | */ |
| 36 | private String accessMode; | 33 | private String accessMode; |
| 37 | /** | 34 | /** |
| 38 | - * 是否是标准品类的必选服务。 | ||
| 39 | - * | ||
| 40 | - * - true:是 | ||
| 41 | - * - false:否 | 35 | + * 是否是标准品类的必选服务 |
| 42 | */ | 36 | */ |
| 43 | private Boolean required; | 37 | private Boolean required; |
| 44 | /** | 38 | /** |
yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/thingmodel/vo/IotProductThingModelPageReqVO.java renamed to 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; | @@ -2,7 +2,7 @@ package cn.iocoder.yudao.module.iot.controller.admin.thingmodel.vo; | ||
| 2 | 2 | ||
| 3 | import cn.iocoder.yudao.framework.common.pojo.PageParam; | 3 | import cn.iocoder.yudao.framework.common.pojo.PageParam; |
| 4 | import cn.iocoder.yudao.framework.common.validation.InEnum; | 4 | import cn.iocoder.yudao.framework.common.validation.InEnum; |
| 5 | -import cn.iocoder.yudao.module.iot.enums.thingmodel.IotProductThingModelTypeEnum; | 5 | +import cn.iocoder.yudao.module.iot.enums.thingmodel.IotThingModelTypeEnum; |
| 6 | import io.swagger.v3.oas.annotations.media.Schema; | 6 | import io.swagger.v3.oas.annotations.media.Schema; |
| 7 | import jakarta.validation.constraints.NotNull; | 7 | import jakarta.validation.constraints.NotNull; |
| 8 | import lombok.Data; | 8 | import lombok.Data; |
| @@ -13,7 +13,7 @@ import lombok.ToString; | @@ -13,7 +13,7 @@ import lombok.ToString; | ||
| 13 | @Data | 13 | @Data |
| 14 | @EqualsAndHashCode(callSuper = true) | 14 | @EqualsAndHashCode(callSuper = true) |
| 15 | @ToString(callSuper = true) | 15 | @ToString(callSuper = true) |
| 16 | -public class IotProductThingModelPageReqVO extends PageParam { | 16 | +public class IotThingModelPageReqVO extends PageParam { |
| 17 | 17 | ||
| 18 | @Schema(description = "功能标识") | 18 | @Schema(description = "功能标识") |
| 19 | private String identifier; | 19 | private String identifier; |
| @@ -22,7 +22,7 @@ public class IotProductThingModelPageReqVO extends PageParam { | @@ -22,7 +22,7 @@ public class IotProductThingModelPageReqVO extends PageParam { | ||
| 22 | private String name; | 22 | private String name; |
| 23 | 23 | ||
| 24 | @Schema(description = "功能类型", example = "1") | 24 | @Schema(description = "功能类型", example = "1") |
| 25 | - @InEnum(IotProductThingModelTypeEnum.class) | 25 | + @InEnum(IotThingModelTypeEnum.class) |
| 26 | private Integer type; | 26 | private Integer type; |
| 27 | 27 | ||
| 28 | @Schema(description = "产品ID", requiredMode = Schema.RequiredMode.REQUIRED) | 28 | @Schema(description = "产品ID", requiredMode = Schema.RequiredMode.REQUIRED) |
yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/thingmodel/vo/IotProductThingModelRespVO.java renamed to 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; | @@ -13,7 +13,7 @@ import java.time.LocalDateTime; | ||
| 13 | @Schema(description = "管理后台 - IoT 产品物模型 Response VO") | 13 | @Schema(description = "管理后台 - IoT 产品物模型 Response VO") |
| 14 | @Data | 14 | @Data |
| 15 | @ExcelIgnoreUnannotated | 15 | @ExcelIgnoreUnannotated |
| 16 | -public class IotProductThingModelRespVO { | 16 | +public class IotThingModelRespVO { |
| 17 | 17 | ||
| 18 | @Schema(description = "产品ID", requiredMode = Schema.RequiredMode.REQUIRED, example = "21816") | 18 | @Schema(description = "产品ID", requiredMode = Schema.RequiredMode.REQUIRED, example = "21816") |
| 19 | @ExcelProperty("产品ID") | 19 | @ExcelProperty("产品ID") |
yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/thingmodel/vo/IotProductThingModelSaveReqVO.java renamed to 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; | @@ -4,7 +4,7 @@ import cn.iocoder.yudao.framework.common.validation.InEnum; | ||
| 4 | import cn.iocoder.yudao.module.iot.controller.admin.thingmodel.model.ThingModelEvent; | 4 | import cn.iocoder.yudao.module.iot.controller.admin.thingmodel.model.ThingModelEvent; |
| 5 | import cn.iocoder.yudao.module.iot.controller.admin.thingmodel.model.ThingModelProperty; | 5 | import cn.iocoder.yudao.module.iot.controller.admin.thingmodel.model.ThingModelProperty; |
| 6 | import cn.iocoder.yudao.module.iot.controller.admin.thingmodel.model.ThingModelService; | 6 | import cn.iocoder.yudao.module.iot.controller.admin.thingmodel.model.ThingModelService; |
| 7 | -import cn.iocoder.yudao.module.iot.enums.thingmodel.IotProductThingModelTypeEnum; | 7 | +import cn.iocoder.yudao.module.iot.enums.thingmodel.IotThingModelTypeEnum; |
| 8 | import io.swagger.v3.oas.annotations.media.Schema; | 8 | import io.swagger.v3.oas.annotations.media.Schema; |
| 9 | import jakarta.validation.constraints.NotEmpty; | 9 | import jakarta.validation.constraints.NotEmpty; |
| 10 | import jakarta.validation.constraints.NotNull; | 10 | import jakarta.validation.constraints.NotNull; |
| @@ -12,7 +12,7 @@ import lombok.Data; | @@ -12,7 +12,7 @@ import lombok.Data; | ||
| 12 | 12 | ||
| 13 | @Schema(description = "管理后台 - IoT 产品物模型新增/修改 Request VO") | 13 | @Schema(description = "管理后台 - IoT 产品物模型新增/修改 Request VO") |
| 14 | @Data | 14 | @Data |
| 15 | -public class IotProductThingModelSaveReqVO { | 15 | +public class IotThingModelSaveReqVO { |
| 16 | 16 | ||
| 17 | @Schema(description = "编号", example = "1") | 17 | @Schema(description = "编号", example = "1") |
| 18 | private Long id; | 18 | private Long id; |
| @@ -38,7 +38,7 @@ public class IotProductThingModelSaveReqVO { | @@ -38,7 +38,7 @@ public class IotProductThingModelSaveReqVO { | ||
| 38 | 38 | ||
| 39 | @Schema(description = "功能类型", requiredMode = Schema.RequiredMode.REQUIRED) | 39 | @Schema(description = "功能类型", requiredMode = Schema.RequiredMode.REQUIRED) |
| 40 | @NotNull(message = "功能类型不能为空") | 40 | @NotNull(message = "功能类型不能为空") |
| 41 | - @InEnum(IotProductThingModelTypeEnum.class) | 41 | + @InEnum(IotThingModelTypeEnum.class) |
| 42 | private Integer type; | 42 | private Integer type; |
| 43 | 43 | ||
| 44 | @Schema(description = "属性", requiredMode = Schema.RequiredMode.REQUIRED) | 44 | @Schema(description = "属性", requiredMode = Schema.RequiredMode.REQUIRED) |
yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/convert/thingmodel/IotProductThingModelConvert.java renamed to 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; | @@ -3,10 +3,10 @@ package cn.iocoder.yudao.module.iot.convert.thingmodel; | ||
| 3 | import cn.iocoder.yudao.module.iot.controller.admin.thingmodel.model.ThingModelEvent; | 3 | import cn.iocoder.yudao.module.iot.controller.admin.thingmodel.model.ThingModelEvent; |
| 4 | import cn.iocoder.yudao.module.iot.controller.admin.thingmodel.model.ThingModelProperty; | 4 | import cn.iocoder.yudao.module.iot.controller.admin.thingmodel.model.ThingModelProperty; |
| 5 | import cn.iocoder.yudao.module.iot.controller.admin.thingmodel.model.ThingModelService; | 5 | import cn.iocoder.yudao.module.iot.controller.admin.thingmodel.model.ThingModelService; |
| 6 | -import cn.iocoder.yudao.module.iot.controller.admin.thingmodel.vo.IotProductThingModelRespVO; | ||
| 7 | -import cn.iocoder.yudao.module.iot.controller.admin.thingmodel.vo.IotProductThingModelSaveReqVO; | ||
| 8 | -import cn.iocoder.yudao.module.iot.dal.dataobject.thingmodel.IotProductThingModelDO; | ||
| 9 | -import cn.iocoder.yudao.module.iot.enums.thingmodel.IotProductThingModelTypeEnum; | 6 | +import cn.iocoder.yudao.module.iot.controller.admin.thingmodel.vo.IotThingModelRespVO; |
| 7 | +import cn.iocoder.yudao.module.iot.controller.admin.thingmodel.vo.IotThingModelSaveReqVO; | ||
| 8 | +import cn.iocoder.yudao.module.iot.dal.dataobject.thingmodel.IotThingModelDO; | ||
| 9 | +import cn.iocoder.yudao.module.iot.enums.thingmodel.IotThingModelTypeEnum; | ||
| 10 | import org.mapstruct.Mapper; | 10 | import org.mapstruct.Mapper; |
| 11 | import org.mapstruct.Mapping; | 11 | import org.mapstruct.Mapping; |
| 12 | import org.mapstruct.Named; | 12 | import org.mapstruct.Named; |
| @@ -16,44 +16,44 @@ import java.util.List; | @@ -16,44 +16,44 @@ import java.util.List; | ||
| 16 | import java.util.Objects; | 16 | import java.util.Objects; |
| 17 | 17 | ||
| 18 | @Mapper | 18 | @Mapper |
| 19 | -public interface IotProductThingModelConvert { | 19 | +public interface IotThingModelConvert { |
| 20 | 20 | ||
| 21 | - IotProductThingModelConvert INSTANCE = Mappers.getMapper(IotProductThingModelConvert.class); | 21 | + IotThingModelConvert INSTANCE = Mappers.getMapper(IotThingModelConvert.class); |
| 22 | 22 | ||
| 23 | // 将 SaveReqVO 转换为 DO | 23 | // 将 SaveReqVO 转换为 DO |
| 24 | @Mapping(target = "property", expression = "java(convertToProperty(bean))") | 24 | @Mapping(target = "property", expression = "java(convertToProperty(bean))") |
| 25 | @Mapping(target = "event", expression = "java(convertToEvent(bean))") | 25 | @Mapping(target = "event", expression = "java(convertToEvent(bean))") |
| 26 | @Mapping(target = "service", expression = "java(convertToService(bean))") | 26 | @Mapping(target = "service", expression = "java(convertToService(bean))") |
| 27 | - IotProductThingModelDO convert(IotProductThingModelSaveReqVO bean); | 27 | + IotThingModelDO convert(IotThingModelSaveReqVO bean); |
| 28 | 28 | ||
| 29 | // 将 DO 转换为 RespVO | 29 | // 将 DO 转换为 RespVO |
| 30 | @Mapping(target = "property", source = "property") | 30 | @Mapping(target = "property", source = "property") |
| 31 | @Mapping(target = "event", source = "event") | 31 | @Mapping(target = "event", source = "event") |
| 32 | @Mapping(target = "service", source = "service") | 32 | @Mapping(target = "service", source = "service") |
| 33 | - IotProductThingModelRespVO convert(IotProductThingModelDO bean); | 33 | + IotThingModelRespVO convert(IotThingModelDO bean); |
| 34 | 34 | ||
| 35 | // 批量转换 | 35 | // 批量转换 |
| 36 | - List<IotProductThingModelRespVO> convertList(List<IotProductThingModelDO> list); | 36 | + List<IotThingModelRespVO> convertList(List<IotThingModelDO> list); |
| 37 | 37 | ||
| 38 | @Named("convertToProperty") | 38 | @Named("convertToProperty") |
| 39 | - default ThingModelProperty convertToProperty(IotProductThingModelSaveReqVO bean) { | ||
| 40 | - if (Objects.equals(bean.getType(), IotProductThingModelTypeEnum.PROPERTY.getType())) { | 39 | + default ThingModelProperty convertToProperty(IotThingModelSaveReqVO bean) { |
| 40 | + if (Objects.equals(bean.getType(), IotThingModelTypeEnum.PROPERTY.getType())) { | ||
| 41 | return bean.getProperty(); | 41 | return bean.getProperty(); |
| 42 | } | 42 | } |
| 43 | return null; | 43 | return null; |
| 44 | } | 44 | } |
| 45 | 45 | ||
| 46 | @Named("convertToEvent") | 46 | @Named("convertToEvent") |
| 47 | - default ThingModelEvent convertToEvent(IotProductThingModelSaveReqVO bean) { | ||
| 48 | - if (Objects.equals(bean.getType(), IotProductThingModelTypeEnum.EVENT.getType())) { | 47 | + default ThingModelEvent convertToEvent(IotThingModelSaveReqVO bean) { |
| 48 | + if (Objects.equals(bean.getType(), IotThingModelTypeEnum.EVENT.getType())) { | ||
| 49 | return bean.getEvent(); | 49 | return bean.getEvent(); |
| 50 | } | 50 | } |
| 51 | return null; | 51 | return null; |
| 52 | } | 52 | } |
| 53 | 53 | ||
| 54 | @Named("convertToService") | 54 | @Named("convertToService") |
| 55 | - default ThingModelService convertToService(IotProductThingModelSaveReqVO bean) { | ||
| 56 | - if (Objects.equals(bean.getType(), IotProductThingModelTypeEnum.SERVICE.getType())) { | 55 | + default ThingModelService convertToService(IotThingModelSaveReqVO bean) { |
| 56 | + if (Objects.equals(bean.getType(), IotThingModelTypeEnum.SERVICE.getType())) { | ||
| 57 | return bean.getService(); | 57 | return bean.getService(); |
| 58 | } | 58 | } |
| 59 | return null; | 59 | return null; |
yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/dataobject/device/IotDeviceDataDO.java
| 1 | package cn.iocoder.yudao.module.iot.dal.dataobject.device; | 1 | package cn.iocoder.yudao.module.iot.dal.dataobject.device; |
| 2 | 2 | ||
| 3 | import cn.iocoder.yudao.module.iot.dal.dataobject.product.IotProductDO; | 3 | import cn.iocoder.yudao.module.iot.dal.dataobject.product.IotProductDO; |
| 4 | -import cn.iocoder.yudao.module.iot.dal.dataobject.thingmodel.IotProductThingModelDO; | 4 | +import cn.iocoder.yudao.module.iot.dal.dataobject.thingmodel.IotThingModelDO; |
| 5 | import lombok.AllArgsConstructor; | 5 | import lombok.AllArgsConstructor; |
| 6 | import lombok.Builder; | 6 | import lombok.Builder; |
| 7 | import lombok.Data; | 7 | import lombok.Data; |
| @@ -30,7 +30,7 @@ public class IotDeviceDataDO { | @@ -30,7 +30,7 @@ public class IotDeviceDataDO { | ||
| 30 | /** | 30 | /** |
| 31 | * 物模型编号 | 31 | * 物模型编号 |
| 32 | * <p> | 32 | * <p> |
| 33 | - * 关联 {@link IotProductThingModelDO#getId()} | 33 | + * 关联 {@link IotThingModelDO#getId()} |
| 34 | */ | 34 | */ |
| 35 | private Long thingModelId; | 35 | private Long thingModelId; |
| 36 | 36 | ||
| @@ -51,21 +51,21 @@ public class IotDeviceDataDO { | @@ -51,21 +51,21 @@ public class IotDeviceDataDO { | ||
| 51 | /** | 51 | /** |
| 52 | * 属性标识符 | 52 | * 属性标识符 |
| 53 | * <p> | 53 | * <p> |
| 54 | - * 关联 {@link IotProductThingModelDO#getIdentifier()} | 54 | + * 关联 {@link IotThingModelDO#getIdentifier()} |
| 55 | */ | 55 | */ |
| 56 | private String identifier; | 56 | private String identifier; |
| 57 | 57 | ||
| 58 | /** | 58 | /** |
| 59 | * 属性名称 | 59 | * 属性名称 |
| 60 | * <p> | 60 | * <p> |
| 61 | - * 关联 {@link IotProductThingModelDO#getName()} | 61 | + * 关联 {@link IotThingModelDO#getName()} |
| 62 | */ | 62 | */ |
| 63 | private String name; | 63 | private String name; |
| 64 | 64 | ||
| 65 | /** | 65 | /** |
| 66 | * 数据类型 | 66 | * 数据类型 |
| 67 | * <p> | 67 | * <p> |
| 68 | - * 关联 {@link IotProductThingModelDO#getProperty()#getDataType()} | 68 | + * 关联 {@link IotThingModelDO#getProperty()#getDataType()} |
| 69 | */ | 69 | */ |
| 70 | private String dataType; | 70 | private String dataType; |
| 71 | 71 |
yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/dataobject/plugininfo/PluginInfoDO.java
| 1 | package cn.iocoder.yudao.module.iot.dal.dataobject.plugininfo; | 1 | package cn.iocoder.yudao.module.iot.dal.dataobject.plugininfo; |
| 2 | 2 | ||
| 3 | import cn.iocoder.yudao.framework.mybatis.core.dataobject.BaseDO; | 3 | import cn.iocoder.yudao.framework.mybatis.core.dataobject.BaseDO; |
| 4 | +import cn.iocoder.yudao.module.iot.enums.plugin.IotPluginDeployTypeEnum; | ||
| 5 | +import cn.iocoder.yudao.module.iot.enums.plugin.IotPluginStatusEnum; | ||
| 6 | +import cn.iocoder.yudao.module.iot.enums.plugin.IotPluginTypeEnum; | ||
| 4 | import com.baomidou.mybatisplus.annotation.KeySequence; | 7 | import com.baomidou.mybatisplus.annotation.KeySequence; |
| 5 | import com.baomidou.mybatisplus.annotation.TableId; | 8 | import com.baomidou.mybatisplus.annotation.TableId; |
| 6 | import com.baomidou.mybatisplus.annotation.TableName; | 9 | import com.baomidou.mybatisplus.annotation.TableName; |
| @@ -26,11 +29,10 @@ public class PluginInfoDO extends BaseDO { | @@ -26,11 +29,10 @@ public class PluginInfoDO extends BaseDO { | ||
| 26 | */ | 29 | */ |
| 27 | @TableId | 30 | @TableId |
| 28 | private Long id; | 31 | private Long id; |
| 29 | - // TODO @haohao:这个是不是改成类似 key 之类的字段哈? | ||
| 30 | /** | 32 | /** |
| 31 | - * 插件包 ID | 33 | + * 插件包标识符 |
| 32 | */ | 34 | */ |
| 33 | - private String pluginId; | 35 | + private String pluginKey; |
| 34 | /** | 36 | /** |
| 35 | * 插件名称 | 37 | * 插件名称 |
| 36 | */ | 38 | */ |
| @@ -41,22 +43,23 @@ public class PluginInfoDO extends BaseDO { | @@ -41,22 +43,23 @@ public class PluginInfoDO extends BaseDO { | ||
| 41 | private String description; | 43 | private String description; |
| 42 | /** | 44 | /** |
| 43 | * 部署方式 | 45 | * 部署方式 |
| 46 | + * <p> | ||
| 47 | + * 枚举 {@link IotPluginDeployTypeEnum} | ||
| 44 | */ | 48 | */ |
| 45 | - // TODO @haohao:枚举 | ||
| 46 | private Integer deployType; | 49 | private Integer deployType; |
| 47 | /** | 50 | /** |
| 48 | * 插件包文件名 | 51 | * 插件包文件名 |
| 49 | */ | 52 | */ |
| 50 | - // TODO @haohao:是不是叫 fileName 哈?避免后续有别的字段,类似 fileUrl? | ||
| 51 | - private String file; | 53 | + private String fileName; |
| 52 | /** | 54 | /** |
| 53 | * 插件版本 | 55 | * 插件版本 |
| 54 | */ | 56 | */ |
| 55 | private String version; | 57 | private String version; |
| 56 | /** | 58 | /** |
| 57 | * 插件类型 | 59 | * 插件类型 |
| 60 | + * <p> | ||
| 61 | + * 枚举 {@link IotPluginTypeEnum} | ||
| 58 | */ | 62 | */ |
| 59 | - // TODO @haohao:枚举 | ||
| 60 | private Integer type; | 63 | private Integer type; |
| 61 | /** | 64 | /** |
| 62 | * 设备插件协议类型 | 65 | * 设备插件协议类型 |
| @@ -64,8 +67,9 @@ public class PluginInfoDO extends BaseDO { | @@ -64,8 +67,9 @@ public class PluginInfoDO extends BaseDO { | ||
| 64 | private String protocol; | 67 | private String protocol; |
| 65 | /** | 68 | /** |
| 66 | * 状态 | 69 | * 状态 |
| 70 | + * <p> | ||
| 71 | + * 枚举 {@link IotPluginStatusEnum} | ||
| 67 | */ | 72 | */ |
| 68 | - // TODO @haohao:枚举 | ||
| 69 | private Integer status; | 73 | private Integer status; |
| 70 | /** | 74 | /** |
| 71 | * 插件配置项描述信息 | 75 | * 插件配置项描述信息 |
yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/dataobject/plugininstance/PluginInstanceDO.java
| 1 | package cn.iocoder.yudao.module.iot.dal.dataobject.plugininstance; | 1 | package cn.iocoder.yudao.module.iot.dal.dataobject.plugininstance; |
| 2 | 2 | ||
| 3 | import cn.iocoder.yudao.framework.mybatis.core.dataobject.BaseDO; | 3 | import cn.iocoder.yudao.framework.mybatis.core.dataobject.BaseDO; |
| 4 | +import cn.iocoder.yudao.module.iot.dal.dataobject.plugininfo.PluginInfoDO; | ||
| 4 | import com.baomidou.mybatisplus.annotation.KeySequence; | 5 | import com.baomidou.mybatisplus.annotation.KeySequence; |
| 5 | import com.baomidou.mybatisplus.annotation.TableId; | 6 | import com.baomidou.mybatisplus.annotation.TableId; |
| 6 | import com.baomidou.mybatisplus.annotation.TableName; | 7 | import com.baomidou.mybatisplus.annotation.TableName; |
| 7 | import lombok.*; | 8 | import lombok.*; |
| 8 | 9 | ||
| 9 | -// TODO @haohao:一些必要的关联、枚举 | 10 | + |
| 10 | /** | 11 | /** |
| 11 | * IoT 插件实例 DO | 12 | * IoT 插件实例 DO |
| 12 | * | 13 | * |
| @@ -33,6 +34,8 @@ public class PluginInstanceDO extends BaseDO { | @@ -33,6 +34,8 @@ public class PluginInstanceDO extends BaseDO { | ||
| 33 | private String mainId; | 34 | private String mainId; |
| 34 | /** | 35 | /** |
| 35 | * 插件id | 36 | * 插件id |
| 37 | + * <p> | ||
| 38 | + * 关联 {@link PluginInfoDO#getId()} | ||
| 36 | */ | 39 | */ |
| 37 | private Long pluginId; | 40 | private Long pluginId; |
| 38 | /** | 41 | /** |
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 | @@ -5,7 +5,8 @@ import cn.iocoder.yudao.module.iot.controller.admin.thingmodel.model.ThingModelR | ||
| 5 | 5 | ||
| 6 | import java.util.HashMap; | 6 | import java.util.HashMap; |
| 7 | import java.util.List; | 7 | import java.util.List; |
| 8 | -import java.util.stream.Collectors; | 8 | + |
| 9 | +import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertList; | ||
| 9 | 10 | ||
| 10 | /** | 11 | /** |
| 11 | * FieldParser 类用于解析和转换物模型字段到 TDengine 字段 | 12 | * FieldParser 类用于解析和转换物模型字段到 TDengine 字段 |
| @@ -15,6 +16,7 @@ public class FieldParser { | @@ -15,6 +16,7 @@ public class FieldParser { | ||
| 15 | /** | 16 | /** |
| 16 | * 物模型到td数据类型映射 | 17 | * 物模型到td数据类型映射 |
| 17 | */ | 18 | */ |
| 19 | + @Deprecated | ||
| 18 | private static final HashMap<String, String> TYPE_MAPPING = new HashMap<>() { | 20 | private static final HashMap<String, String> TYPE_MAPPING = new HashMap<>() { |
| 19 | { | 21 | { |
| 20 | put("INT", "INT"); | 22 | put("INT", "INT"); |
| @@ -52,10 +54,7 @@ public class FieldParser { | @@ -52,10 +54,7 @@ public class FieldParser { | ||
| 52 | * @return 字段列表 | 54 | * @return 字段列表 |
| 53 | */ | 55 | */ |
| 54 | public static List<TdFieldDO> parse(ThingModelRespVO thingModel) { | 56 | public static List<TdFieldDO> parse(ThingModelRespVO thingModel) { |
| 55 | - // TODO @puhui999:是不是使用 convertList | ||
| 56 | - return thingModel.getModel().getProperties().stream() | ||
| 57 | - .map(FieldParser::parse) | ||
| 58 | - .collect(Collectors.toList()); | 57 | + return convertList(thingModel.getModel().getProperties(), FieldParser::parse); |
| 59 | } | 58 | } |
| 60 | 59 | ||
| 61 | /** | 60 | /** |
| @@ -65,18 +64,18 @@ public class FieldParser { | @@ -65,18 +64,18 @@ public class FieldParser { | ||
| 65 | * @return 转换后的 TDengine 字段对象列表 | 64 | * @return 转换后的 TDengine 字段对象列表 |
| 66 | */ | 65 | */ |
| 67 | public static List<TdFieldDO> parse(List<List<Object>> rows) { | 66 | public static List<TdFieldDO> parse(List<List<Object>> rows) { |
| 68 | - // TODO @puhui999:是不是使用 convertList | ||
| 69 | - return rows.stream().map(row -> { | 67 | + return convertList(rows, row -> { |
| 70 | String type = row.get(1).toString().toUpperCase(); | 68 | String type = row.get(1).toString().toUpperCase(); |
| 71 | // TODO @puhui999:"NCHAR" 最好枚举下 | 69 | // TODO @puhui999:"NCHAR" 最好枚举下 |
| 72 | int dataLength = "NCHAR".equals(type) ? Integer.parseInt(row.get(2).toString()) : -1; | 70 | int dataLength = "NCHAR".equals(type) ? Integer.parseInt(row.get(2).toString()) : -1; |
| 73 | return new TdFieldDO(row.get(0).toString(), type, dataLength); | 71 | return new TdFieldDO(row.get(0).toString(), type, dataLength); |
| 74 | - }).collect(Collectors.toList()); | 72 | + }); |
| 75 | } | 73 | } |
| 76 | 74 | ||
| 77 | /** | 75 | /** |
| 78 | * 获取字段字义 | 76 | * 获取字段字义 |
| 79 | */ | 77 | */ |
| 78 | + @Deprecated | ||
| 80 | public static String getFieldDefine(TdFieldDO field) { | 79 | public static String getFieldDefine(TdFieldDO field) { |
| 81 | return "`" + field.getFieldName() + "`" + " " | 80 | return "`" + field.getFieldName() + "`" + " " |
| 82 | + (field.getDataLength() > 0 ? String.format("%s(%d)", field.getDataType(), field.getDataLength()) | 81 | + (field.getDataLength() > 0 ? String.format("%s(%d)", field.getDataType(), field.getDataLength()) |
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; | @@ -2,13 +2,12 @@ package cn.iocoder.yudao.module.iot.dal.dataobject.tdengine; | ||
| 2 | 2 | ||
| 3 | import lombok.Data; | 3 | import lombok.Data; |
| 4 | 4 | ||
| 5 | -import java.util.Set; | ||
| 6 | - | ||
| 7 | // TODO @haohao:类似这个,其实可以参考 mybatis plus,querywrapper,搞个 TdEngineQueryWrapper。这样看起来会更好懂。 | 5 | // TODO @haohao:类似这个,其实可以参考 mybatis plus,querywrapper,搞个 TdEngineQueryWrapper。这样看起来会更好懂。 |
| 8 | /** | 6 | /** |
| 9 | * 查询DO | 7 | * 查询DO |
| 10 | */ | 8 | */ |
| 11 | @Data | 9 | @Data |
| 10 | +@Deprecated | ||
| 12 | public class SelectDO { | 11 | public class SelectDO { |
| 13 | 12 | ||
| 14 | // TODO @haoha:database 是个单词 | 13 | // TODO @haoha:database 是个单词 |
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; | @@ -6,6 +6,7 @@ import java.util.Map; | ||
| 6 | 6 | ||
| 7 | // TODO @haohao:类似 SelectDO 的想法,只是它是返回。ps:貌似可以在 tdengine 里面,创建一个 query 包,放这种比较特殊的查询和结果对象。dataobject 更多还是实际存储的结构化的 do | 7 | // TODO @haohao:类似 SelectDO 的想法,只是它是返回。ps:貌似可以在 tdengine 里面,创建一个 query 包,放这种比较特殊的查询和结果对象。dataobject 更多还是实际存储的结构化的 do |
| 8 | @Data | 8 | @Data |
| 9 | +@Deprecated | ||
| 9 | public class SelectVisualDO { | 10 | public class SelectVisualDO { |
| 10 | 11 | ||
| 11 | /** | 12 | /** |
yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/dataobject/tdengine/TagsSelectDO.java
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; | @@ -5,6 +5,7 @@ import lombok.Builder; | ||
| 5 | import lombok.Data; | 5 | import lombok.Data; |
| 6 | import lombok.NoArgsConstructor; | 6 | import lombok.NoArgsConstructor; |
| 7 | 7 | ||
| 8 | +// TODO 芋艿:看看是不是后续简化掉。 | ||
| 8 | /** | 9 | /** |
| 9 | * TD 引擎的字段 | 10 | * TD 引擎的字段 |
| 10 | */ | 11 | */ |
| @@ -12,6 +13,7 @@ import lombok.NoArgsConstructor; | @@ -12,6 +13,7 @@ import lombok.NoArgsConstructor; | ||
| 12 | @NoArgsConstructor | 13 | @NoArgsConstructor |
| 13 | @AllArgsConstructor | 14 | @AllArgsConstructor |
| 14 | @Builder | 15 | @Builder |
| 16 | +@Deprecated | ||
| 15 | public class TdFieldDO { | 17 | public class TdFieldDO { |
| 16 | 18 | ||
| 17 | /** | 19 | /** |
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; | @@ -12,6 +12,7 @@ import java.util.List; | ||
| 12 | @Data | 12 | @Data |
| 13 | @NoArgsConstructor | 13 | @NoArgsConstructor |
| 14 | @AllArgsConstructor | 14 | @AllArgsConstructor |
| 15 | +@Deprecated | ||
| 15 | public class TdResponse { | 16 | public class TdResponse { |
| 16 | 17 | ||
| 17 | public static final int CODE_SUCCESS = 0; | 18 | public static final int CODE_SUCCESS = 0; |
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; | @@ -13,6 +13,7 @@ import org.springframework.stereotype.Service; | ||
| 13 | */ | 13 | */ |
| 14 | @Slf4j | 14 | @Slf4j |
| 15 | @Service | 15 | @Service |
| 16 | +@Deprecated // TODO 芋艿:貌似没用到 | ||
| 16 | public class TdRestApi { | 17 | public class TdRestApi { |
| 17 | 18 | ||
| 18 | @Value("${spring.datasource.dynamic.datasource.tdengine.url}") | 19 | @Value("${spring.datasource.dynamic.datasource.tdengine.url}") |
yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/dataobject/tdengine/TdTableDO.java
yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/dataobject/thingmodel/IotProductThingModelDO.java renamed to 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 | @@ -5,7 +5,7 @@ import cn.iocoder.yudao.module.iot.controller.admin.thingmodel.model.ThingModelE | ||
| 5 | import cn.iocoder.yudao.module.iot.controller.admin.thingmodel.model.ThingModelProperty; | 5 | import cn.iocoder.yudao.module.iot.controller.admin.thingmodel.model.ThingModelProperty; |
| 6 | import cn.iocoder.yudao.module.iot.controller.admin.thingmodel.model.ThingModelService; | 6 | import cn.iocoder.yudao.module.iot.controller.admin.thingmodel.model.ThingModelService; |
| 7 | import cn.iocoder.yudao.module.iot.dal.dataobject.product.IotProductDO; | 7 | import cn.iocoder.yudao.module.iot.dal.dataobject.product.IotProductDO; |
| 8 | -import cn.iocoder.yudao.module.iot.enums.thingmodel.IotProductThingModelTypeEnum; | 8 | +import cn.iocoder.yudao.module.iot.enums.thingmodel.IotThingModelTypeEnum; |
| 9 | import com.baomidou.mybatisplus.annotation.KeySequence; | 9 | import com.baomidou.mybatisplus.annotation.KeySequence; |
| 10 | import com.baomidou.mybatisplus.annotation.TableField; | 10 | import com.baomidou.mybatisplus.annotation.TableField; |
| 11 | import com.baomidou.mybatisplus.annotation.TableId; | 11 | import com.baomidou.mybatisplus.annotation.TableId; |
| @@ -19,17 +19,17 @@ import lombok.NoArgsConstructor; | @@ -19,17 +19,17 @@ import lombok.NoArgsConstructor; | ||
| 19 | /** | 19 | /** |
| 20 | * IoT 产品物模型功能 DO | 20 | * IoT 产品物模型功能 DO |
| 21 | * <p> | 21 | * <p> |
| 22 | - * 每个 {@link IotProductDO} 和 {@link IotProductThingModelDO} 是“一对多”的关系,它的每个属性、事件、服务都对应一条记录 | 22 | + * 每个 {@link IotProductDO} 和 {@link IotThingModelDO} 是“一对多”的关系,它的每个属性、事件、服务都对应一条记录 |
| 23 | * | 23 | * |
| 24 | * @author 芋道源码 | 24 | * @author 芋道源码 |
| 25 | */ | 25 | */ |
| 26 | -@TableName(value = "iot_product_thing_model", autoResultMap = true) | ||
| 27 | -@KeySequence("iot_product_thing_model_seq") // 用于 Oracle、PostgreSQL、Kingbase、DB2、H2 数据库的主键自增。如果是 MySQL 等数据库,可不写。 | 26 | +@TableName(value = "iot_thing_model", autoResultMap = true) |
| 27 | +@KeySequence("iot_thing_model_seq") // 用于 Oracle、PostgreSQL、Kingbase、DB2、H2 数据库的主键自增。如果是 MySQL 等数据库,可不写。 | ||
| 28 | @Data | 28 | @Data |
| 29 | @Builder | 29 | @Builder |
| 30 | @NoArgsConstructor | 30 | @NoArgsConstructor |
| 31 | @AllArgsConstructor | 31 | @AllArgsConstructor |
| 32 | -public class IotProductThingModelDO extends BaseDO { | 32 | +public class IotThingModelDO extends BaseDO { |
| 33 | 33 | ||
| 34 | /** | 34 | /** |
| 35 | * 物模型功能编号 | 35 | * 物模型功能编号 |
| @@ -66,7 +66,7 @@ public class IotProductThingModelDO extends BaseDO { | @@ -66,7 +66,7 @@ public class IotProductThingModelDO extends BaseDO { | ||
| 66 | /** | 66 | /** |
| 67 | * 功能类型 | 67 | * 功能类型 |
| 68 | * <p> | 68 | * <p> |
| 69 | - * 枚举 {@link IotProductThingModelTypeEnum} | 69 | + * 枚举 {@link IotThingModelTypeEnum} |
| 70 | */ | 70 | */ |
| 71 | private Integer type; | 71 | private Integer type; |
| 72 | 72 |
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; | @@ -6,6 +6,7 @@ import cn.iocoder.yudao.framework.mybatis.core.mapper.BaseMapperX; | ||
| 6 | import cn.iocoder.yudao.framework.mybatis.core.query.LambdaQueryWrapperX; | 6 | import cn.iocoder.yudao.framework.mybatis.core.query.LambdaQueryWrapperX; |
| 7 | import cn.iocoder.yudao.module.iot.controller.admin.device.vo.device.IotDevicePageReqVO; | 7 | import cn.iocoder.yudao.module.iot.controller.admin.device.vo.device.IotDevicePageReqVO; |
| 8 | import cn.iocoder.yudao.module.iot.dal.dataobject.device.IotDeviceDO; | 8 | import cn.iocoder.yudao.module.iot.dal.dataobject.device.IotDeviceDO; |
| 9 | +import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; | ||
| 9 | import org.apache.ibatis.annotations.Mapper; | 10 | import org.apache.ibatis.annotations.Mapper; |
| 10 | 11 | ||
| 11 | import java.util.List; | 12 | import java.util.List; |
| @@ -47,7 +48,8 @@ public interface IotDeviceMapper extends BaseMapperX<IotDeviceDO> { | @@ -47,7 +48,8 @@ public interface IotDeviceMapper extends BaseMapperX<IotDeviceDO> { | ||
| 47 | } | 48 | } |
| 48 | 49 | ||
| 49 | default IotDeviceDO selectByDeviceKey(String deviceKey) { | 50 | default IotDeviceDO selectByDeviceKey(String deviceKey) { |
| 50 | - return selectOne(IotDeviceDO::getDeviceKey, deviceKey); | 51 | + return selectOne(new LambdaQueryWrapper<IotDeviceDO>() |
| 52 | + .apply("LOWER(device_key) = {0}", deviceKey.toLowerCase())); | ||
| 51 | } | 53 | } |
| 52 | 54 | ||
| 53 | default List<IotDeviceDO> selectList(Integer deviceType) { | 55 | default List<IotDeviceDO> selectList(Integer deviceType) { |
yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/mysql/plugininfo/PluginInfoMapper.java renamed to yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/mysql/plugin/PluginInfoMapper.java
| 1 | -package cn.iocoder.yudao.module.iot.dal.mysql.plugininfo; | 1 | +package cn.iocoder.yudao.module.iot.dal.mysql.plugin; |
| 2 | 2 | ||
| 3 | import cn.iocoder.yudao.framework.common.pojo.PageResult; | 3 | import cn.iocoder.yudao.framework.common.pojo.PageResult; |
| 4 | import cn.iocoder.yudao.framework.mybatis.core.query.LambdaQueryWrapperX; | 4 | import cn.iocoder.yudao.framework.mybatis.core.query.LambdaQueryWrapperX; |
| 5 | import cn.iocoder.yudao.framework.mybatis.core.mapper.BaseMapperX; | 5 | import cn.iocoder.yudao.framework.mybatis.core.mapper.BaseMapperX; |
| 6 | import cn.iocoder.yudao.module.iot.dal.dataobject.plugininfo.PluginInfoDO; | 6 | import cn.iocoder.yudao.module.iot.dal.dataobject.plugininfo.PluginInfoDO; |
| 7 | import org.apache.ibatis.annotations.Mapper; | 7 | import org.apache.ibatis.annotations.Mapper; |
| 8 | -import cn.iocoder.yudao.module.iot.controller.admin.plugininfo.vo.*; | 8 | +import cn.iocoder.yudao.module.iot.controller.admin.plugin.vo.*; |
| 9 | 9 | ||
| 10 | /** | 10 | /** |
| 11 | * IoT 插件信息 Mapper | 11 | * IoT 插件信息 Mapper |
| @@ -17,19 +17,8 @@ public interface PluginInfoMapper extends BaseMapperX<PluginInfoDO> { | @@ -17,19 +17,8 @@ public interface PluginInfoMapper extends BaseMapperX<PluginInfoDO> { | ||
| 17 | 17 | ||
| 18 | default PageResult<PluginInfoDO> selectPage(PluginInfoPageReqVO reqVO) { | 18 | default PageResult<PluginInfoDO> selectPage(PluginInfoPageReqVO reqVO) { |
| 19 | return selectPage(reqVO, new LambdaQueryWrapperX<PluginInfoDO>() | 19 | return selectPage(reqVO, new LambdaQueryWrapperX<PluginInfoDO>() |
| 20 | - .eqIfPresent(PluginInfoDO::getPluginId, reqVO.getPluginId()) | ||
| 21 | .likeIfPresent(PluginInfoDO::getName, reqVO.getName()) | 20 | .likeIfPresent(PluginInfoDO::getName, reqVO.getName()) |
| 22 | - .eqIfPresent(PluginInfoDO::getDescription, reqVO.getDescription()) | ||
| 23 | - .eqIfPresent(PluginInfoDO::getDeployType, reqVO.getDeployType()) | ||
| 24 | - .eqIfPresent(PluginInfoDO::getFile, reqVO.getFile()) | ||
| 25 | - .eqIfPresent(PluginInfoDO::getVersion, reqVO.getVersion()) | ||
| 26 | - .eqIfPresent(PluginInfoDO::getType, reqVO.getType()) | ||
| 27 | - .eqIfPresent(PluginInfoDO::getProtocol, reqVO.getProtocol()) | ||
| 28 | .eqIfPresent(PluginInfoDO::getStatus, reqVO.getStatus()) | 21 | .eqIfPresent(PluginInfoDO::getStatus, reqVO.getStatus()) |
| 29 | - .eqIfPresent(PluginInfoDO::getConfigSchema, reqVO.getConfigSchema()) | ||
| 30 | - .eqIfPresent(PluginInfoDO::getConfig, reqVO.getConfig()) | ||
| 31 | - .eqIfPresent(PluginInfoDO::getScript, reqVO.getScript()) | ||
| 32 | - .betweenIfPresent(PluginInfoDO::getCreateTime, reqVO.getCreateTime()) | ||
| 33 | .orderByDesc(PluginInfoDO::getId)); | 22 | .orderByDesc(PluginInfoDO::getId)); |
| 34 | } | 23 | } |
| 35 | 24 |
yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/mysql/plugininstance/PluginInstanceMapper.java renamed to yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/mysql/plugin/PluginInstanceMapper.java
| 1 | -package cn.iocoder.yudao.module.iot.dal.mysql.plugininstance; | 1 | +package cn.iocoder.yudao.module.iot.dal.mysql.plugin; |
| 2 | 2 | ||
| 3 | import cn.iocoder.yudao.framework.common.pojo.PageResult; | 3 | import cn.iocoder.yudao.framework.common.pojo.PageResult; |
| 4 | import cn.iocoder.yudao.framework.mybatis.core.query.LambdaQueryWrapperX; | 4 | import cn.iocoder.yudao.framework.mybatis.core.query.LambdaQueryWrapperX; |
| 5 | import cn.iocoder.yudao.framework.mybatis.core.mapper.BaseMapperX; | 5 | import cn.iocoder.yudao.framework.mybatis.core.mapper.BaseMapperX; |
| 6 | +import cn.iocoder.yudao.module.iot.controller.admin.plugin.vo.PluginInstancePageReqVO; | ||
| 6 | import cn.iocoder.yudao.module.iot.dal.dataobject.plugininstance.PluginInstanceDO; | 7 | import cn.iocoder.yudao.module.iot.dal.dataobject.plugininstance.PluginInstanceDO; |
| 7 | import org.apache.ibatis.annotations.Mapper; | 8 | import org.apache.ibatis.annotations.Mapper; |
| 8 | -import cn.iocoder.yudao.module.iot.controller.admin.plugininstance.vo.*; | ||
| 9 | 9 | ||
| 10 | /** | 10 | /** |
| 11 | * IoT 插件实例 Mapper | 11 | * IoT 插件实例 Mapper |
| @@ -15,6 +15,12 @@ import cn.iocoder.yudao.module.iot.controller.admin.plugininstance.vo.*; | @@ -15,6 +15,12 @@ import cn.iocoder.yudao.module.iot.controller.admin.plugininstance.vo.*; | ||
| 15 | @Mapper | 15 | @Mapper |
| 16 | public interface PluginInstanceMapper extends BaseMapperX<PluginInstanceDO> { | 16 | public interface PluginInstanceMapper extends BaseMapperX<PluginInstanceDO> { |
| 17 | 17 | ||
| 18 | + default PluginInstanceDO selectByMainIdAndPluginId(String mainId, Long pluginId) { | ||
| 19 | + return selectOne(new LambdaQueryWrapperX<PluginInstanceDO>() | ||
| 20 | + .eq(PluginInstanceDO::getMainId, mainId) | ||
| 21 | + .eq(PluginInstanceDO::getPluginId, pluginId)); | ||
| 22 | + } | ||
| 23 | + | ||
| 18 | default PageResult<PluginInstanceDO> selectPage(PluginInstancePageReqVO reqVO) { | 24 | default PageResult<PluginInstanceDO> selectPage(PluginInstancePageReqVO reqVO) { |
| 19 | return selectPage(reqVO, new LambdaQueryWrapperX<PluginInstanceDO>() | 25 | return selectPage(reqVO, new LambdaQueryWrapperX<PluginInstanceDO>() |
| 20 | .eqIfPresent(PluginInstanceDO::getMainId, reqVO.getMainId()) | 26 | .eqIfPresent(PluginInstanceDO::getMainId, reqVO.getMainId()) |
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; | @@ -5,6 +5,7 @@ import cn.iocoder.yudao.framework.mybatis.core.mapper.BaseMapperX; | ||
| 5 | import cn.iocoder.yudao.framework.mybatis.core.query.LambdaQueryWrapperX; | 5 | import cn.iocoder.yudao.framework.mybatis.core.query.LambdaQueryWrapperX; |
| 6 | import cn.iocoder.yudao.module.iot.controller.admin.product.vo.product.IotProductPageReqVO; | 6 | import cn.iocoder.yudao.module.iot.controller.admin.product.vo.product.IotProductPageReqVO; |
| 7 | import cn.iocoder.yudao.module.iot.dal.dataobject.product.IotProductDO; | 7 | import cn.iocoder.yudao.module.iot.dal.dataobject.product.IotProductDO; |
| 8 | +import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; | ||
| 8 | import org.apache.ibatis.annotations.Mapper; | 9 | import org.apache.ibatis.annotations.Mapper; |
| 9 | 10 | ||
| 10 | /** | 11 | /** |
| @@ -23,7 +24,8 @@ public interface IotProductMapper extends BaseMapperX<IotProductDO> { | @@ -23,7 +24,8 @@ public interface IotProductMapper extends BaseMapperX<IotProductDO> { | ||
| 23 | } | 24 | } |
| 24 | 25 | ||
| 25 | default IotProductDO selectByProductKey(String productKey) { | 26 | default IotProductDO selectByProductKey(String productKey) { |
| 26 | - return selectOne(IotProductDO::getProductKey, productKey); | 27 | + return selectOne(new LambdaQueryWrapper<IotProductDO>() |
| 28 | + .apply("LOWER(product_key) = {0}", productKey.toLowerCase())); | ||
| 27 | } | 29 | } |
| 28 | 30 | ||
| 29 | } | 31 | } |
| 30 | \ No newline at end of file | 32 | \ No newline at end of file |
yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/mysql/thingmodel/IotProductThingModelMapper.java deleted
| 1 | -package cn.iocoder.yudao.module.iot.dal.mysql.thingmodel; | ||
| 2 | - | ||
| 3 | -import cn.iocoder.yudao.framework.common.pojo.PageResult; | ||
| 4 | -import cn.iocoder.yudao.framework.mybatis.core.mapper.BaseMapperX; | ||
| 5 | -import cn.iocoder.yudao.framework.mybatis.core.query.LambdaQueryWrapperX; | ||
| 6 | -import cn.iocoder.yudao.module.iot.controller.admin.thingmodel.vo.IotProductThingModelPageReqVO; | ||
| 7 | -import cn.iocoder.yudao.module.iot.dal.dataobject.thingmodel.IotProductThingModelDO; | ||
| 8 | -import org.apache.ibatis.annotations.Mapper; | ||
| 9 | - | ||
| 10 | -import java.util.List; | ||
| 11 | - | ||
| 12 | -/** | ||
| 13 | - * IoT 产品物模型 Mapper | ||
| 14 | - * | ||
| 15 | - * @author 芋道源码 | ||
| 16 | - */ | ||
| 17 | -@Mapper | ||
| 18 | -public interface IotProductThingModelMapper extends BaseMapperX<IotProductThingModelDO> { | ||
| 19 | - | ||
| 20 | - default PageResult<IotProductThingModelDO> selectPage(IotProductThingModelPageReqVO reqVO) { | ||
| 21 | - return selectPage(reqVO, new LambdaQueryWrapperX<IotProductThingModelDO>() | ||
| 22 | - .eqIfPresent(IotProductThingModelDO::getIdentifier, reqVO.getIdentifier()) | ||
| 23 | - .likeIfPresent(IotProductThingModelDO::getName, reqVO.getName()) | ||
| 24 | - .eqIfPresent(IotProductThingModelDO::getType, reqVO.getType()) | ||
| 25 | - .eqIfPresent(IotProductThingModelDO::getProductId, reqVO.getProductId()) | ||
| 26 | - .notIn(IotProductThingModelDO::getIdentifier, "get", "set", "post") | ||
| 27 | - .orderByDesc(IotProductThingModelDO::getId)); | ||
| 28 | - } | ||
| 29 | - | ||
| 30 | - default IotProductThingModelDO selectByProductIdAndIdentifier(Long productId, String identifier) { | ||
| 31 | - return selectOne(IotProductThingModelDO::getProductId, productId, | ||
| 32 | - IotProductThingModelDO::getIdentifier, identifier); | ||
| 33 | - } | ||
| 34 | - | ||
| 35 | - default List<IotProductThingModelDO> selectListByProductId(Long productId) { | ||
| 36 | - return selectList(IotProductThingModelDO::getProductId, productId); | ||
| 37 | - } | ||
| 38 | - | ||
| 39 | - default List<IotProductThingModelDO> selectListByProductIdAndType(Long productId, Integer type) { | ||
| 40 | - return selectList(IotProductThingModelDO::getProductId, productId, | ||
| 41 | - IotProductThingModelDO::getType, type); | ||
| 42 | - } | ||
| 43 | - | ||
| 44 | - default List<IotProductThingModelDO> selectListByProductIdAndIdentifiersAndTypes(Long productId, | ||
| 45 | - List<String> identifiers, | ||
| 46 | - List<Integer> types) { | ||
| 47 | - return selectList(new LambdaQueryWrapperX<IotProductThingModelDO>() | ||
| 48 | - .eq(IotProductThingModelDO::getProductId, productId) | ||
| 49 | - .in(IotProductThingModelDO::getIdentifier, identifiers) | ||
| 50 | - .in(IotProductThingModelDO::getType, types)); | ||
| 51 | - } | ||
| 52 | - | ||
| 53 | - default IotProductThingModelDO selectByProductIdAndName(Long productId, String name) { | ||
| 54 | - return selectOne(IotProductThingModelDO::getProductId, productId, | ||
| 55 | - IotProductThingModelDO::getName, name); | ||
| 56 | - } | ||
| 57 | - | ||
| 58 | - default List<IotProductThingModelDO> selectListByProductKey(String productKey) { | ||
| 59 | - return selectList(IotProductThingModelDO::getProductKey, productKey); | ||
| 60 | - } | ||
| 61 | - | ||
| 62 | -} | ||
| 63 | \ No newline at end of file | 0 | \ No newline at end of file |
yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/mysql/thingmodel/IotThingModelMapper.java
0 → 100644
| 1 | +package cn.iocoder.yudao.module.iot.dal.mysql.thingmodel; | ||
| 2 | + | ||
| 3 | +import cn.iocoder.yudao.framework.common.pojo.PageResult; | ||
| 4 | +import cn.iocoder.yudao.framework.mybatis.core.mapper.BaseMapperX; | ||
| 5 | +import cn.iocoder.yudao.framework.mybatis.core.query.LambdaQueryWrapperX; | ||
| 6 | +import cn.iocoder.yudao.module.iot.controller.admin.thingmodel.vo.IotThingModelPageReqVO; | ||
| 7 | +import cn.iocoder.yudao.module.iot.dal.dataobject.thingmodel.IotThingModelDO; | ||
| 8 | +import org.apache.ibatis.annotations.Mapper; | ||
| 9 | + | ||
| 10 | +import java.util.List; | ||
| 11 | + | ||
| 12 | +/** | ||
| 13 | + * IoT 产品物模型 Mapper | ||
| 14 | + * | ||
| 15 | + * @author 芋道源码 | ||
| 16 | + */ | ||
| 17 | +@Mapper | ||
| 18 | +public interface IotThingModelMapper extends BaseMapperX<IotThingModelDO> { | ||
| 19 | + | ||
| 20 | + default PageResult<IotThingModelDO> selectPage(IotThingModelPageReqVO reqVO) { | ||
| 21 | + return selectPage(reqVO, new LambdaQueryWrapperX<IotThingModelDO>() | ||
| 22 | + .eqIfPresent(IotThingModelDO::getIdentifier, reqVO.getIdentifier()) | ||
| 23 | + .likeIfPresent(IotThingModelDO::getName, reqVO.getName()) | ||
| 24 | + .eqIfPresent(IotThingModelDO::getType, reqVO.getType()) | ||
| 25 | + .eqIfPresent(IotThingModelDO::getProductId, reqVO.getProductId()) | ||
| 26 | + // TODO @芋艿:看看要不要加枚举 | ||
| 27 | + .notIn(IotThingModelDO::getIdentifier, "get", "set", "post") | ||
| 28 | + .orderByDesc(IotThingModelDO::getId)); | ||
| 29 | + } | ||
| 30 | + | ||
| 31 | + default IotThingModelDO selectByProductIdAndIdentifier(Long productId, String identifier) { | ||
| 32 | + return selectOne(IotThingModelDO::getProductId, productId, | ||
| 33 | + IotThingModelDO::getIdentifier, identifier); | ||
| 34 | + } | ||
| 35 | + | ||
| 36 | + default List<IotThingModelDO> selectListByProductId(Long productId) { | ||
| 37 | + return selectList(IotThingModelDO::getProductId, productId); | ||
| 38 | + } | ||
| 39 | + | ||
| 40 | + default List<IotThingModelDO> selectListByProductIdAndType(Long productId, Integer type) { | ||
| 41 | + return selectList(IotThingModelDO::getProductId, productId, | ||
| 42 | + IotThingModelDO::getType, type); | ||
| 43 | + } | ||
| 44 | + | ||
| 45 | + default List<IotThingModelDO> selectListByProductIdAndIdentifiersAndTypes(Long productId, | ||
| 46 | + List<String> identifiers, | ||
| 47 | + List<Integer> types) { | ||
| 48 | + return selectList(new LambdaQueryWrapperX<IotThingModelDO>() | ||
| 49 | + .eq(IotThingModelDO::getProductId, productId) | ||
| 50 | + .in(IotThingModelDO::getIdentifier, identifiers) | ||
| 51 | + .in(IotThingModelDO::getType, types)); | ||
| 52 | + } | ||
| 53 | + | ||
| 54 | + default IotThingModelDO selectByProductIdAndName(Long productId, String name) { | ||
| 55 | + return selectOne(IotThingModelDO::getProductId, productId, | ||
| 56 | + IotThingModelDO::getName, name); | ||
| 57 | + } | ||
| 58 | + | ||
| 59 | + default List<IotThingModelDO> selectListByProductKey(String productKey) { | ||
| 60 | + return selectList(IotThingModelDO::getProductKey, productKey); | ||
| 61 | + } | ||
| 62 | + | ||
| 63 | +} | ||
| 0 | \ No newline at end of file | 64 | \ No newline at end of file |
yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/tdengine/IotDevicePropertyDataMapper.java
0 → 100644
| 1 | +package cn.iocoder.yudao.module.iot.dal.tdengine; | ||
| 2 | + | ||
| 3 | +import cn.hutool.core.collection.CollUtil; | ||
| 4 | +import cn.hutool.core.util.ObjectUtil; | ||
| 5 | +import cn.iocoder.yudao.module.iot.framework.tdengine.core.TDengineTableField; | ||
| 6 | +import cn.iocoder.yudao.module.iot.framework.tdengine.core.annotation.TDengineDS; | ||
| 7 | +import com.baomidou.mybatisplus.annotation.InterceptorIgnore; | ||
| 8 | +import org.apache.ibatis.annotations.Mapper; | ||
| 9 | +import org.apache.ibatis.annotations.Param; | ||
| 10 | + | ||
| 11 | +import java.util.ArrayList; | ||
| 12 | +import java.util.List; | ||
| 13 | +import java.util.stream.Collectors; | ||
| 14 | + | ||
| 15 | +@Mapper | ||
| 16 | +@TDengineDS | ||
| 17 | +@InterceptorIgnore(tenantLine = "true") // 避免 SQL 解析,因为 JSqlParser 对 TDengine 的 SQL 解析会报错 | ||
| 18 | +public interface IotDevicePropertyDataMapper { | ||
| 19 | + | ||
| 20 | + List<TDengineTableField> getProductPropertySTableFieldList(@Param("productKey") String productKey); | ||
| 21 | + | ||
| 22 | + void createProductPropertySTable(@Param("productKey") String productKey, | ||
| 23 | + @Param("fields") List<TDengineTableField> fields); | ||
| 24 | + | ||
| 25 | + @SuppressWarnings("SimplifyStreamApiCallChains") // 保持 JDK8 兼容性 | ||
| 26 | + default void alterProductPropertySTable(String productKey, | ||
| 27 | + List<TDengineTableField> oldFields, | ||
| 28 | + List<TDengineTableField> newFields) { | ||
| 29 | + oldFields.removeIf(field -> TDengineTableField.FIELD_TS.equals(field.getField()) | ||
| 30 | + || TDengineTableField.FIELD_DEVICE_KEY.equals(field.getField())); | ||
| 31 | + List<TDengineTableField> addFields = newFields.stream().filter( // 新增的字段 | ||
| 32 | + newField -> oldFields.stream().noneMatch(oldField -> oldField.getField().equals(newField.getField()))) | ||
| 33 | + .collect(Collectors.toList()); | ||
| 34 | + List<TDengineTableField> dropFields = oldFields.stream().filter( // 删除的字段 | ||
| 35 | + oldField -> newFields.stream().noneMatch(n -> n.getField().equals(oldField.getField()))) | ||
| 36 | + .collect(Collectors.toList()); | ||
| 37 | + List<TDengineTableField> modifyTypeFields = new ArrayList<>(); // 变更类型的字段 | ||
| 38 | + List<TDengineTableField> modifyLengthFields = new ArrayList<>(); // 变更长度的字段 | ||
| 39 | + newFields.forEach(newField -> { | ||
| 40 | + TDengineTableField oldField = CollUtil.findOne(oldFields, field -> field.getField().equals(newField.getField())); | ||
| 41 | + if (oldField == null) { | ||
| 42 | + return; | ||
| 43 | + } | ||
| 44 | + if (ObjectUtil.notEqual(oldField.getType(), newField.getType())) { | ||
| 45 | + modifyTypeFields.add(newField); | ||
| 46 | + return; | ||
| 47 | + } | ||
| 48 | + if (newField.getLength()!= null) { | ||
| 49 | + if (newField.getLength() > oldField.getLength()) { | ||
| 50 | + modifyLengthFields.add(newField); | ||
| 51 | + } else if (newField.getLength() < oldField.getLength()) { | ||
| 52 | + // 特殊:TDengine 长度修改时,只允许变长,所以此时认为是修改类型 | ||
| 53 | + modifyTypeFields.add(newField); | ||
| 54 | + } | ||
| 55 | + } | ||
| 56 | + }); | ||
| 57 | + | ||
| 58 | + // 执行 | ||
| 59 | + addFields.forEach(field -> alterProductPropertySTableAddField(productKey, field)); | ||
| 60 | + dropFields.forEach(field -> alterProductPropertySTableDropField(productKey, field)); | ||
| 61 | + modifyLengthFields.forEach(field -> alterProductPropertySTableModifyField(productKey, field)); | ||
| 62 | + modifyTypeFields.forEach(field -> { | ||
| 63 | + alterProductPropertySTableDropField(productKey, field); | ||
| 64 | + alterProductPropertySTableAddField(productKey, field); | ||
| 65 | + }); | ||
| 66 | + } | ||
| 67 | + | ||
| 68 | + void alterProductPropertySTableAddField(@Param("productKey") String productKey, | ||
| 69 | + @Param("field") TDengineTableField field); | ||
| 70 | + | ||
| 71 | + void alterProductPropertySTableModifyField(@Param("productKey") String productKey, | ||
| 72 | + @Param("field") TDengineTableField field); | ||
| 73 | + | ||
| 74 | + void alterProductPropertySTableDropField(@Param("productKey") String productKey, | ||
| 75 | + @Param("field") TDengineTableField field); | ||
| 76 | + | ||
| 77 | +} |
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 { | @@ -96,7 +96,6 @@ public interface TdEngineDDLMapper { | ||
| 96 | @TenantIgnore | 96 | @TenantIgnore |
| 97 | void modifyColumnWidthForSuperTable(TdTableDO superTable); | 97 | void modifyColumnWidthForSuperTable(TdTableDO superTable); |
| 98 | 98 | ||
| 99 | - | ||
| 100 | /** | 99 | /** |
| 101 | * 修改超级表 - 为超级表添加标签 | 100 | * 修改超级表 - 为超级表添加标签 |
| 102 | * | 101 | * |
yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/emq/service/EmqxServiceImpl.java
| 1 | package cn.iocoder.yudao.module.iot.emq.service; | 1 | package cn.iocoder.yudao.module.iot.emq.service; |
| 2 | 2 | ||
| 3 | -import cn.iocoder.yudao.framework.common.util.json.JsonUtils; | ||
| 4 | -import cn.iocoder.yudao.module.iot.service.device.IotDeviceDataService; | 3 | +import cn.iocoder.yudao.module.iot.service.device.IotDevicePropertyDataService; |
| 5 | import jakarta.annotation.Resource; | 4 | import jakarta.annotation.Resource; |
| 6 | import lombok.extern.slf4j.Slf4j; | 5 | import lombok.extern.slf4j.Slf4j; |
| 7 | import org.eclipse.paho.client.mqttv3.MqttClient; | 6 | import org.eclipse.paho.client.mqttv3.MqttClient; |
| @@ -21,7 +20,7 @@ import org.springframework.stereotype.Service; | @@ -21,7 +20,7 @@ import org.springframework.stereotype.Service; | ||
| 21 | public class EmqxServiceImpl implements EmqxService { | 20 | public class EmqxServiceImpl implements EmqxService { |
| 22 | 21 | ||
| 23 | @Resource | 22 | @Resource |
| 24 | - private IotDeviceDataService iotDeviceDataService; | 23 | + private IotDevicePropertyDataService iotDeviceDataService; |
| 25 | 24 | ||
| 26 | // TODO 多线程处理消息 | 25 | // TODO 多线程处理消息 |
| 27 | @Override | 26 | @Override |
yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/framework/plugin/SpringConfiguration.java deleted
| 1 | -package cn.iocoder.yudao.module.iot.framework.plugin; | ||
| 2 | - | ||
| 3 | -import org.pf4j.spring.SpringPluginManager; | ||
| 4 | -import org.springframework.context.annotation.Bean; | ||
| 5 | -import org.springframework.context.annotation.Configuration; | ||
| 6 | -import org.springframework.context.annotation.DependsOn; | ||
| 7 | - | ||
| 8 | -@Configuration | ||
| 9 | -public class SpringConfiguration { | ||
| 10 | - | ||
| 11 | - @Bean | ||
| 12 | - @DependsOn("serviceRegistryInitializedMarker") | ||
| 13 | - public SpringPluginManager pluginManager() { | ||
| 14 | - return new SpringPluginManager(); | ||
| 15 | - } | ||
| 16 | - | ||
| 17 | -} | ||
| 18 | \ No newline at end of file | 0 | \ No newline at end of file |
yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/framework/plugin/ServiceRegistryConfiguration.java renamed to yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/framework/plugin/UnifiedConfiguration.java
| 1 | package cn.iocoder.yudao.module.iot.framework.plugin; | 1 | package cn.iocoder.yudao.module.iot.framework.plugin; |
| 2 | 2 | ||
| 3 | -import cn.iocoder.yudao.module.iot.api.DeviceDataApi; | ||
| 4 | import cn.iocoder.yudao.module.iot.api.ServiceRegistry; | 3 | import cn.iocoder.yudao.module.iot.api.ServiceRegistry; |
| 4 | +import cn.iocoder.yudao.module.iot.api.device.DeviceDataApi; | ||
| 5 | +import cn.iocoder.yudao.module.iot.framework.plugin.listener.CustomPluginStateListener; | ||
| 5 | import lombok.extern.slf4j.Slf4j; | 6 | import lombok.extern.slf4j.Slf4j; |
| 7 | +import org.pf4j.spring.SpringPluginManager; | ||
| 6 | import org.springframework.context.annotation.Bean; | 8 | import org.springframework.context.annotation.Bean; |
| 7 | import org.springframework.context.annotation.Configuration; | 9 | import org.springframework.context.annotation.Configuration; |
| 10 | +import org.springframework.context.annotation.DependsOn; | ||
| 8 | 11 | ||
| 9 | -import javax.annotation.PostConstruct; | ||
| 10 | import javax.annotation.Resource; | 12 | import javax.annotation.Resource; |
| 11 | 13 | ||
| 12 | @Slf4j | 14 | @Slf4j |
| 13 | @Configuration | 15 | @Configuration |
| 14 | -public class ServiceRegistryConfiguration { | 16 | +public class UnifiedConfiguration { |
| 17 | + | ||
| 18 | + private static final String SERVICE_REGISTRY_INITIALIZED_MARKER = "serviceRegistryInitializedMarker"; | ||
| 15 | 19 | ||
| 16 | @Resource | 20 | @Resource |
| 17 | private DeviceDataApi deviceDataApi; | 21 | private DeviceDataApi deviceDataApi; |
| 18 | 22 | ||
| 19 | - @PostConstruct | ||
| 20 | - public void init() { | ||
| 21 | - // 将主程序中的 DeviceDataApi 实例注册到 ServiceRegistry | 23 | + @Bean(SERVICE_REGISTRY_INITIALIZED_MARKER) |
| 24 | + public Object serviceRegistryInitializedMarker() { | ||
| 22 | ServiceRegistry.registerService(DeviceDataApi.class, deviceDataApi); | 25 | ServiceRegistry.registerService(DeviceDataApi.class, deviceDataApi); |
| 23 | log.info("[init][将 DeviceDataApi 实例注册到 ServiceRegistry 中]"); | 26 | log.info("[init][将 DeviceDataApi 实例注册到 ServiceRegistry 中]"); |
| 27 | + return new Object(); | ||
| 24 | } | 28 | } |
| 25 | 29 | ||
| 26 | - /** | ||
| 27 | - * 定义一个标记用的 Bean,用于表示 ServiceRegistry 已初始化完成 | ||
| 28 | - */ | ||
| 29 | - @Bean("serviceRegistryInitializedMarker") | ||
| 30 | - public Object serviceRegistryInitializedMarker() { | ||
| 31 | - // 返回任意对象即可,这里返回null都可以,但最好返回个实际对象 | ||
| 32 | - return new Object(); | 30 | + @Bean |
| 31 | + @DependsOn(SERVICE_REGISTRY_INITIALIZED_MARKER) | ||
| 32 | + public SpringPluginManager pluginManager() { | ||
| 33 | + log.info("[init][实例化 SpringPluginManager]"); | ||
| 34 | + SpringPluginManager springPluginManager = new SpringPluginManager(); | ||
| 35 | + springPluginManager.addPluginStateListener(new CustomPluginStateListener()); | ||
| 36 | + return springPluginManager; | ||
| 33 | } | 37 | } |
| 38 | + | ||
| 34 | } | 39 | } |
| 35 | \ No newline at end of file | 40 | \ No newline at end of file |
yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/framework/plugin/listener/CustomPluginStateListener.java
0 → 100644
| 1 | +package cn.iocoder.yudao.module.iot.framework.plugin.listener; | ||
| 2 | + | ||
| 3 | +import lombok.extern.slf4j.Slf4j; | ||
| 4 | +import org.pf4j.PluginStateEvent; | ||
| 5 | +import org.pf4j.PluginStateListener; | ||
| 6 | +import org.springframework.stereotype.Component; | ||
| 7 | + | ||
| 8 | +@Component | ||
| 9 | +@Slf4j | ||
| 10 | +public class CustomPluginStateListener implements PluginStateListener { | ||
| 11 | + | ||
| 12 | + @Override | ||
| 13 | + public void pluginStateChanged(PluginStateEvent event) { | ||
| 14 | + // 1. 获取插件ID | ||
| 15 | + String pluginId = event.getPlugin().getPluginId(); | ||
| 16 | + // 2. 获取插件旧状态 | ||
| 17 | + String oldState = event.getOldState().toString(); | ||
| 18 | + // 3. 获取插件新状态 | ||
| 19 | + String newState = event.getPluginState().toString(); | ||
| 20 | + // 4. 打印日志信息 | ||
| 21 | + log.info("插件的状态 '{}' 已更改为 '{}' 至 '{}'", pluginId, oldState, newState); | ||
| 22 | + } | ||
| 23 | + | ||
| 24 | +} | ||
| 0 | \ No newline at end of file | 25 | \ No newline at end of file |
yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/framework/tdengine/core/TDengineTableField.java
0 → 100644
| 1 | +package cn.iocoder.yudao.module.iot.framework.tdengine.core; | ||
| 2 | + | ||
| 3 | +import lombok.AllArgsConstructor; | ||
| 4 | +import lombok.Data; | ||
| 5 | +import lombok.NoArgsConstructor; | ||
| 6 | + | ||
| 7 | +/** | ||
| 8 | + * TDEngine 表字段 | ||
| 9 | + */ | ||
| 10 | +@Data | ||
| 11 | +@NoArgsConstructor | ||
| 12 | +@AllArgsConstructor | ||
| 13 | +public class TDengineTableField { | ||
| 14 | + | ||
| 15 | + /** | ||
| 16 | + * 字段名 - TDengine 默认 ts 字段,默认会被 TDengine 创建 | ||
| 17 | + */ | ||
| 18 | + public static final String FIELD_TS = "ts"; | ||
| 19 | + | ||
| 20 | + /** | ||
| 21 | + * 字段名 - 我们系统定义的 device_key 字段,非 TDengine 默认字段 | ||
| 22 | + */ | ||
| 23 | + public static final String FIELD_DEVICE_KEY = "device_key"; | ||
| 24 | + | ||
| 25 | + public static final String TYPE_TINYINT = "TINYINT"; | ||
| 26 | + public static final String TYPE_INT = "INT"; | ||
| 27 | + public static final String TYPE_FLOAT = "FLOAT"; | ||
| 28 | + public static final String TYPE_DOUBLE = "DOUBLE"; | ||
| 29 | + public static final String TYPE_BOOL = "BOOL"; | ||
| 30 | + public static final String TYPE_NCHAR = "NCHAR"; | ||
| 31 | + public static final String TYPE_TIMESTAMP = "TIMESTAMP"; | ||
| 32 | + | ||
| 33 | + /** | ||
| 34 | + * 注释 - TAG 字段 | ||
| 35 | + */ | ||
| 36 | + public static final String NOTE_TAG = "TAG"; | ||
| 37 | + | ||
| 38 | + /** | ||
| 39 | + * 字段名 | ||
| 40 | + */ | ||
| 41 | + private String field; | ||
| 42 | + | ||
| 43 | + /** | ||
| 44 | + * 字段类型 | ||
| 45 | + */ | ||
| 46 | + private String type; | ||
| 47 | + | ||
| 48 | + /** | ||
| 49 | + * 字段长度 | ||
| 50 | + */ | ||
| 51 | + private Integer length; | ||
| 52 | + | ||
| 53 | + /** | ||
| 54 | + * 注释 | ||
| 55 | + */ | ||
| 56 | + private String note; | ||
| 57 | + | ||
| 58 | + public TDengineTableField(String field, String type) { | ||
| 59 | + this.field = field; | ||
| 60 | + this.type = type; | ||
| 61 | + } | ||
| 62 | + | ||
| 63 | +} |
yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/framework/tdengine/core/annotation/TDengineDS.java
0 → 100644
| 1 | +package cn.iocoder.yudao.module.iot.framework.tdengine.core.annotation; | ||
| 2 | + | ||
| 3 | +import com.baomidou.dynamic.datasource.annotation.DS; | ||
| 4 | + | ||
| 5 | +import java.lang.annotation.*; | ||
| 6 | + | ||
| 7 | +/** | ||
| 8 | + * TDEngine 数据源 | ||
| 9 | + * | ||
| 10 | + * @author 芋道源码 | ||
| 11 | + */ | ||
| 12 | +@Target({ElementType.TYPE, ElementType.METHOD}) | ||
| 13 | +@Retention(RetentionPolicy.RUNTIME) | ||
| 14 | +@Documented | ||
| 15 | +@DS("tdengine") | ||
| 16 | +public @interface TDengineDS { | ||
| 17 | +} | ||
| 0 | \ No newline at end of file | 18 | \ No newline at end of file |
yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/framework/tdengine/package-info.java
0 → 100644
yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/job/plugin/PluginInstancesJob.java
0 → 100644
| 1 | +package cn.iocoder.yudao.module.iot.job.plugin; | ||
| 2 | + | ||
| 3 | + | ||
| 4 | +import cn.iocoder.yudao.framework.tenant.core.util.TenantUtils; | ||
| 5 | +import cn.iocoder.yudao.module.iot.service.plugin.PluginInstanceService; | ||
| 6 | +import org.springframework.scheduling.annotation.Scheduled; | ||
| 7 | +import org.springframework.stereotype.Component; | ||
| 8 | + | ||
| 9 | +import javax.annotation.Resource; | ||
| 10 | +import java.util.concurrent.TimeUnit; | ||
| 11 | + | ||
| 12 | +/** | ||
| 13 | + * 插件实例 Job | ||
| 14 | + * | ||
| 15 | + * @author 芋道源码 | ||
| 16 | + */ | ||
| 17 | +@Component | ||
| 18 | +public class PluginInstancesJob { | ||
| 19 | + | ||
| 20 | + @Resource | ||
| 21 | + private PluginInstanceService pluginInstanceService; | ||
| 22 | + | ||
| 23 | + @Scheduled(initialDelay = 60, fixedRate = 60, timeUnit = TimeUnit.SECONDS) | ||
| 24 | + public void updatePluginInstances() { | ||
| 25 | + TenantUtils.executeIgnore(() -> { | ||
| 26 | + pluginInstanceService.updatePluginInstances(); | ||
| 27 | + }); | ||
| 28 | + } | ||
| 29 | +} | ||
| 0 | \ No newline at end of file | 30 | \ No newline at end of file |
yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/mq/consumer/simulatesend/SimulateSendConsumer.java
| 1 | package cn.iocoder.yudao.module.iot.mq.consumer.simulatesend; | 1 | package cn.iocoder.yudao.module.iot.mq.consumer.simulatesend; |
| 2 | 2 | ||
| 3 | /** | 3 | /** |
| 4 | + * TODO @alwayssuper:记得实现,还有类注释哈 | ||
| 5 | + * | ||
| 4 | * @author alwayssuper | 6 | * @author alwayssuper |
| 5 | - * @date 2024/12/20 8:04 | 7 | + * @since 2024/12/20 8:04 |
| 6 | */ | 8 | */ |
| 7 | public class SimulateSendConsumer { | 9 | public class SimulateSendConsumer { |
| 8 | } | 10 | } |
yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/device/IotDeviceDataServiceImpl.java deleted
| 1 | -package cn.iocoder.yudao.module.iot.service.device; | ||
| 2 | - | ||
| 3 | -import cn.hutool.core.date.DateUtil; | ||
| 4 | -import cn.hutool.core.util.StrUtil; | ||
| 5 | -import cn.hutool.json.JSONObject; | ||
| 6 | -import cn.iocoder.yudao.framework.common.pojo.PageResult; | ||
| 7 | -import cn.iocoder.yudao.module.iot.controller.admin.device.vo.deviceData.IotDeviceDataPageReqVO; | ||
| 8 | -import cn.iocoder.yudao.module.iot.dal.dataobject.device.IotDeviceDO; | ||
| 9 | -import cn.iocoder.yudao.module.iot.dal.dataobject.device.IotDeviceDataDO; | ||
| 10 | -import cn.iocoder.yudao.module.iot.dal.dataobject.tdengine.SelectVisualDO; | ||
| 11 | -import cn.iocoder.yudao.module.iot.dal.dataobject.tdengine.ThingModelMessage; | ||
| 12 | -import cn.iocoder.yudao.module.iot.dal.dataobject.thingmodel.IotProductThingModelDO; | ||
| 13 | -import cn.iocoder.yudao.module.iot.dal.redis.deviceData.DeviceDataRedisDAO; | ||
| 14 | -import cn.iocoder.yudao.module.iot.dal.tdengine.TdEngineDMLMapper; | ||
| 15 | -import cn.iocoder.yudao.module.iot.enums.IotConstants; | ||
| 16 | -import cn.iocoder.yudao.module.iot.enums.thingmodel.IotProductThingModelTypeEnum; | ||
| 17 | -import cn.iocoder.yudao.module.iot.service.tdengine.IotThingModelMessageService; | ||
| 18 | -import cn.iocoder.yudao.module.iot.service.thingmodel.IotProductThingModelService; | ||
| 19 | -import jakarta.annotation.Resource; | ||
| 20 | -import jakarta.validation.Valid; | ||
| 21 | -import lombok.extern.slf4j.Slf4j; | ||
| 22 | -import org.springframework.beans.factory.annotation.Value; | ||
| 23 | -import org.springframework.stereotype.Service; | ||
| 24 | - | ||
| 25 | -import java.time.ZoneId; | ||
| 26 | -import java.util.ArrayList; | ||
| 27 | -import java.util.HashMap; | ||
| 28 | -import java.util.List; | ||
| 29 | -import java.util.Map; | ||
| 30 | - | ||
| 31 | -import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.filterList; | ||
| 32 | - | ||
| 33 | -@Slf4j | ||
| 34 | -@Service | ||
| 35 | -public class IotDeviceDataServiceImpl implements IotDeviceDataService { | ||
| 36 | - | ||
| 37 | - @Value("${spring.datasource.dynamic.datasource.tdengine.url}") | ||
| 38 | - private String url; | ||
| 39 | - | ||
| 40 | - @Resource | ||
| 41 | - private IotDeviceService deviceService; | ||
| 42 | - @Resource | ||
| 43 | - private IotThingModelMessageService thingModelMessageService; | ||
| 44 | - @Resource | ||
| 45 | - private IotProductThingModelService thingModelService; | ||
| 46 | - @Resource | ||
| 47 | - private TdEngineDMLMapper tdEngineDMLMapper; | ||
| 48 | - | ||
| 49 | - @Resource | ||
| 50 | - private DeviceDataRedisDAO deviceDataRedisDAO; | ||
| 51 | - | ||
| 52 | - @Override | ||
| 53 | - public void saveDeviceData(String productKey, String deviceName, String message) { | ||
| 54 | - // 1. 根据产品 key 和设备名称,获得设备信息 | ||
| 55 | - IotDeviceDO device = deviceService.getDeviceByProductKeyAndDeviceName(productKey, deviceName); | ||
| 56 | - // 2. 解析消息,保存数据 | ||
| 57 | - JSONObject jsonObject = new JSONObject(message); | ||
| 58 | - log.info("[saveDeviceData][productKey({}) deviceName({}) data({})]", productKey, deviceName, jsonObject); | ||
| 59 | - ThingModelMessage thingModelMessage = ThingModelMessage.builder() | ||
| 60 | - .id(jsonObject.getStr("id")) | ||
| 61 | - .sys(jsonObject.get("sys")) | ||
| 62 | - .method(jsonObject.getStr("method")) | ||
| 63 | - .params(jsonObject.get("params")) | ||
| 64 | - .time(jsonObject.getLong("time") == null ? System.currentTimeMillis() : jsonObject.getLong("time")) | ||
| 65 | - .productKey(productKey) | ||
| 66 | - .deviceName(deviceName) | ||
| 67 | - .deviceKey(device.getDeviceKey()) | ||
| 68 | - .build(); | ||
| 69 | - thingModelMessageService.saveThingModelMessage(device, thingModelMessage); | ||
| 70 | - } | ||
| 71 | - | ||
| 72 | - @Override | ||
| 73 | - public List<IotDeviceDataDO> getLatestDeviceProperties(@Valid IotDeviceDataPageReqVO deviceDataReqVO) { | ||
| 74 | - List<IotDeviceDataDO> list = new ArrayList<>(); | ||
| 75 | - // 1. 获取设备信息 | ||
| 76 | - IotDeviceDO device = deviceService.getDevice(deviceDataReqVO.getDeviceId()); | ||
| 77 | - // 2. 获取设备属性最新数据 | ||
| 78 | - List<IotProductThingModelDO> thingModelList = thingModelService.getProductThingModelListByProductKey(device.getProductKey()); | ||
| 79 | - thingModelList = filterList(thingModelList, thingModel -> IotProductThingModelTypeEnum.PROPERTY.getType() | ||
| 80 | - .equals(thingModel.getType())); | ||
| 81 | - | ||
| 82 | - // 3. 过滤标识符和属性名称 | ||
| 83 | - if (deviceDataReqVO.getIdentifier() != null) { | ||
| 84 | - thingModelList = filterList(thingModelList, thingModel -> thingModel.getIdentifier() | ||
| 85 | - .toLowerCase().contains(deviceDataReqVO.getIdentifier().toLowerCase())); | ||
| 86 | - } | ||
| 87 | - if (deviceDataReqVO.getName() != null) { | ||
| 88 | - thingModelList = filterList(thingModelList, thingModel -> thingModel.getName() | ||
| 89 | - .toLowerCase().contains(deviceDataReqVO.getName().toLowerCase())); | ||
| 90 | - } | ||
| 91 | - // 4. 获取设备属性最新数据 | ||
| 92 | - thingModelList.forEach(thingModel -> { | ||
| 93 | - IotDeviceDataDO deviceData = deviceDataRedisDAO.get(device.getProductKey(), device.getDeviceName(), thingModel.getIdentifier()); | ||
| 94 | - if (deviceData == null) { | ||
| 95 | - deviceData = new IotDeviceDataDO(); | ||
| 96 | - deviceData.setProductKey(device.getProductKey()); | ||
| 97 | - deviceData.setDeviceName(device.getDeviceName()); | ||
| 98 | - deviceData.setIdentifier(thingModel.getIdentifier()); | ||
| 99 | - deviceData.setDeviceId(deviceDataReqVO.getDeviceId()); | ||
| 100 | - deviceData.setThingModelId(thingModel.getId()); | ||
| 101 | - deviceData.setName(thingModel.getName()); | ||
| 102 | - deviceData.setDataType(thingModel.getProperty().getDataType()); | ||
| 103 | - } | ||
| 104 | - list.add(deviceData); | ||
| 105 | - }); | ||
| 106 | - return list; | ||
| 107 | - } | ||
| 108 | - | ||
| 109 | - @Override | ||
| 110 | - public PageResult<Map<String, Object>> getHistoryDeviceProperties(IotDeviceDataPageReqVO deviceDataReqVO) { | ||
| 111 | - PageResult<Map<String, Object>> pageResult = new PageResult<>(); | ||
| 112 | - // 1. 获取设备信息 | ||
| 113 | - IotDeviceDO device = deviceService.getDevice(deviceDataReqVO.getDeviceId()); | ||
| 114 | - // 2. 获取设备属性历史数据 | ||
| 115 | - SelectVisualDO selectVisualDO = new SelectVisualDO(); | ||
| 116 | - selectVisualDO.setDataBaseName(getDatabaseName()); | ||
| 117 | - selectVisualDO.setTableName(getDeviceTableName(device.getProductKey(), device.getDeviceName())); | ||
| 118 | - selectVisualDO.setFieldName(deviceDataReqVO.getIdentifier()); | ||
| 119 | - selectVisualDO.setStartTime(DateUtil.date(deviceDataReqVO.getTimes()[0].atZone(ZoneId.systemDefault()).toInstant().toEpochMilli()).getTime()); | ||
| 120 | - selectVisualDO.setEndTime(DateUtil.date(deviceDataReqVO.getTimes()[1].atZone(ZoneId.systemDefault()).toInstant().toEpochMilli()).getTime()); | ||
| 121 | - Map<String, Object> params = new HashMap<>(); | ||
| 122 | - params.put("rows", deviceDataReqVO.getPageSize()); | ||
| 123 | - params.put("page", (deviceDataReqVO.getPageNo() - 1) * deviceDataReqVO.getPageSize()); | ||
| 124 | - selectVisualDO.setParams(params); | ||
| 125 | - pageResult.setList(tdEngineDMLMapper.selectHistoryDataList(selectVisualDO)); | ||
| 126 | - pageResult.setTotal(tdEngineDMLMapper.selectHistoryCount(selectVisualDO)); | ||
| 127 | - return pageResult; | ||
| 128 | - } | ||
| 129 | - | ||
| 130 | - private String getDatabaseName() { | ||
| 131 | - return StrUtil.subAfter(url, "/", true); | ||
| 132 | - } | ||
| 133 | - | ||
| 134 | - private static String getDeviceTableName(String productKey, String deviceName) { | ||
| 135 | - return String.format(IotConstants.DEVICE_TABLE_NAME_FORMAT, productKey, deviceName); | ||
| 136 | - } | ||
| 137 | - | ||
| 138 | -} | ||
| 139 | \ No newline at end of file | 0 | \ No newline at end of file |
yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/device/IotDeviceDataService.java renamed to 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; | @@ -9,11 +9,18 @@ import java.util.List; | ||
| 9 | import java.util.Map; | 9 | import java.util.Map; |
| 10 | 10 | ||
| 11 | /** | 11 | /** |
| 12 | - * IoT 设备数据 Service 接口 | 12 | + * IoT 设备【属性】数据 Service 接口 |
| 13 | * | 13 | * |
| 14 | * @author 芋道源码 | 14 | * @author 芋道源码 |
| 15 | */ | 15 | */ |
| 16 | -public interface IotDeviceDataService { | 16 | +public interface IotDevicePropertyDataService { |
| 17 | + | ||
| 18 | + /** | ||
| 19 | + * 定义设备属性数据的结构 | ||
| 20 | + * | ||
| 21 | + * @param productId 产品编号 | ||
| 22 | + */ | ||
| 23 | + void defineDevicePropertyData(Long productId); | ||
| 17 | 24 | ||
| 18 | /** | 25 | /** |
| 19 | * 保存设备数据 | 26 | * 保存设备数据 |
| @@ -21,7 +28,7 @@ public interface IotDeviceDataService { | @@ -21,7 +28,7 @@ public interface IotDeviceDataService { | ||
| 21 | * @param productKey 产品 key | 28 | * @param productKey 产品 key |
| 22 | * @param deviceName 设备名称 | 29 | * @param deviceName 设备名称 |
| 23 | * @param message 消息 | 30 | * @param message 消息 |
| 24 | - * <p>JSON 格式,参见 <a href="https://help.aliyun.com/zh/iot/user-guide/device-properties-events-and-services?spm=a2c4g.11186623.0.0.3a3335aeUdzkz2#concept-mvc-4tw-y2b">...</a> | 31 | + * <p>参见 <a href="https://help.aliyun.com/zh/iot/user-guide/device-properties-events-and-services?spm=a2c4g.11186623.0.0.3a3335aeUdzkz2#concept-mvc-4tw-y2b">JSON 格式</a> |
| 25 | */ | 32 | */ |
| 26 | void saveDeviceData(String productKey, String deviceName, String message); | 33 | void saveDeviceData(String productKey, String deviceName, String message); |
| 27 | 34 | ||
| @@ -40,4 +47,5 @@ public interface IotDeviceDataService { | @@ -40,4 +47,5 @@ public interface IotDeviceDataService { | ||
| 40 | * @return 设备属性历史数据 | 47 | * @return 设备属性历史数据 |
| 41 | */ | 48 | */ |
| 42 | PageResult<Map<String, Object>> getHistoryDeviceProperties(@Valid IotDeviceDataPageReqVO deviceDataReqVO); | 49 | PageResult<Map<String, Object>> getHistoryDeviceProperties(@Valid IotDeviceDataPageReqVO deviceDataReqVO); |
| 50 | + | ||
| 43 | } | 51 | } |
| 44 | \ No newline at end of file | 52 | \ No newline at end of file |
yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/device/IotDevicePropertyDataServiceImpl.java
0 → 100644
| 1 | +package cn.iocoder.yudao.module.iot.service.device; | ||
| 2 | + | ||
| 3 | +import cn.hutool.core.collection.CollUtil; | ||
| 4 | +import cn.hutool.core.date.DateUtil; | ||
| 5 | +import cn.hutool.core.map.MapUtil; | ||
| 6 | +import cn.hutool.core.util.StrUtil; | ||
| 7 | +import cn.hutool.json.JSONObject; | ||
| 8 | +import cn.iocoder.yudao.framework.common.pojo.PageResult; | ||
| 9 | +import cn.iocoder.yudao.module.iot.controller.admin.device.vo.deviceData.IotDeviceDataPageReqVO; | ||
| 10 | +import cn.iocoder.yudao.module.iot.controller.admin.thingmodel.model.dataType.ThingModelDateOrTextDataSpecs; | ||
| 11 | +import cn.iocoder.yudao.module.iot.dal.dataobject.device.IotDeviceDO; | ||
| 12 | +import cn.iocoder.yudao.module.iot.dal.dataobject.device.IotDeviceDataDO; | ||
| 13 | +import cn.iocoder.yudao.module.iot.dal.dataobject.product.IotProductDO; | ||
| 14 | +import cn.iocoder.yudao.module.iot.dal.dataobject.tdengine.SelectVisualDO; | ||
| 15 | +import cn.iocoder.yudao.module.iot.dal.dataobject.tdengine.ThingModelMessage; | ||
| 16 | +import cn.iocoder.yudao.module.iot.dal.dataobject.thingmodel.IotThingModelDO; | ||
| 17 | +import cn.iocoder.yudao.module.iot.dal.tdengine.IotDevicePropertyDataMapper; | ||
| 18 | +import cn.iocoder.yudao.module.iot.dal.redis.deviceData.DeviceDataRedisDAO; | ||
| 19 | +import cn.iocoder.yudao.module.iot.dal.tdengine.TdEngineDMLMapper; | ||
| 20 | +import cn.iocoder.yudao.module.iot.enums.IotConstants; | ||
| 21 | +import cn.iocoder.yudao.module.iot.enums.thingmodel.IotDataSpecsDataTypeEnum; | ||
| 22 | +import cn.iocoder.yudao.module.iot.enums.thingmodel.IotThingModelTypeEnum; | ||
| 23 | +import cn.iocoder.yudao.module.iot.framework.tdengine.core.TDengineTableField; | ||
| 24 | +import cn.iocoder.yudao.module.iot.service.product.IotProductService; | ||
| 25 | +import cn.iocoder.yudao.module.iot.service.tdengine.IotThingModelMessageService; | ||
| 26 | +import cn.iocoder.yudao.module.iot.service.thingmodel.IotThingModelService; | ||
| 27 | +import jakarta.annotation.Resource; | ||
| 28 | +import jakarta.validation.Valid; | ||
| 29 | +import lombok.extern.slf4j.Slf4j; | ||
| 30 | +import org.springframework.beans.factory.annotation.Value; | ||
| 31 | +import org.springframework.stereotype.Service; | ||
| 32 | + | ||
| 33 | +import java.time.ZoneId; | ||
| 34 | +import java.util.ArrayList; | ||
| 35 | +import java.util.HashMap; | ||
| 36 | +import java.util.List; | ||
| 37 | +import java.util.Map; | ||
| 38 | + | ||
| 39 | +import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertList; | ||
| 40 | +import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.filterList; | ||
| 41 | + | ||
| 42 | +/** | ||
| 43 | + * IoT 设备【属性】数据 Service 实现类 | ||
| 44 | + * | ||
| 45 | + * @author 芋道源码 | ||
| 46 | + */ | ||
| 47 | +@Service | ||
| 48 | +@Slf4j | ||
| 49 | +public class IotDevicePropertyDataServiceImpl implements IotDevicePropertyDataService { | ||
| 50 | + | ||
| 51 | + /** | ||
| 52 | + * 物模型的数据类型,与 TDengine 数据类型的映射关系 | ||
| 53 | + */ | ||
| 54 | + private static final Map<String, String> TYPE_MAPPING = MapUtil.<String, String>builder() | ||
| 55 | + .put(IotDataSpecsDataTypeEnum.INT.getDataType(), TDengineTableField.TYPE_INT) | ||
| 56 | + .put(IotDataSpecsDataTypeEnum.FLOAT.getDataType(), TDengineTableField.TYPE_FLOAT) | ||
| 57 | + .put(IotDataSpecsDataTypeEnum.DOUBLE.getDataType(), TDengineTableField.TYPE_DOUBLE) | ||
| 58 | + .put(IotDataSpecsDataTypeEnum.ENUM.getDataType(), TDengineTableField.TYPE_TINYINT) // TODO 芋艿:为什么要映射为 TINYINT 的说明? | ||
| 59 | + .put( IotDataSpecsDataTypeEnum.BOOL.getDataType(), TDengineTableField.TYPE_TINYINT) // TODO 芋艿:为什么要映射为 TINYINT 的说明? | ||
| 60 | + .put(IotDataSpecsDataTypeEnum.TEXT.getDataType(), TDengineTableField.TYPE_NCHAR) | ||
| 61 | + .put(IotDataSpecsDataTypeEnum.DATE.getDataType(), TDengineTableField.TYPE_TIMESTAMP) | ||
| 62 | + .put(IotDataSpecsDataTypeEnum.STRUCT.getDataType(), TDengineTableField.TYPE_NCHAR) // TODO 芋艿:怎么映射!!!! | ||
| 63 | + .put(IotDataSpecsDataTypeEnum.ARRAY.getDataType(), TDengineTableField.TYPE_NCHAR) // TODO 芋艿:怎么映射!!!! | ||
| 64 | + .build(); | ||
| 65 | + | ||
| 66 | + @Value("${spring.datasource.dynamic.datasource.tdengine.url}") | ||
| 67 | + private String url; | ||
| 68 | + | ||
| 69 | + @Resource | ||
| 70 | + private IotDeviceService deviceService; | ||
| 71 | + @Resource | ||
| 72 | + private IotThingModelMessageService thingModelMessageService; | ||
| 73 | + @Resource | ||
| 74 | + private IotThingModelService thingModelService; | ||
| 75 | + @Resource | ||
| 76 | + private IotProductService productService; | ||
| 77 | + | ||
| 78 | + @Resource | ||
| 79 | + private TdEngineDMLMapper tdEngineDMLMapper; | ||
| 80 | + | ||
| 81 | + @Resource | ||
| 82 | + private DeviceDataRedisDAO deviceDataRedisDAO; | ||
| 83 | + | ||
| 84 | + @Resource | ||
| 85 | + private IotDevicePropertyDataMapper devicePropertyDataMapper; | ||
| 86 | + | ||
| 87 | + @Override | ||
| 88 | + public void defineDevicePropertyData(Long productId) { | ||
| 89 | + // 1.1 查询产品和物模型 | ||
| 90 | + IotProductDO product = productService.validateProductExists(productId); | ||
| 91 | + List<IotThingModelDO> thingModels = filterList(thingModelService.getThingModelListByProductId(productId), | ||
| 92 | + thingModel -> IotThingModelTypeEnum.PROPERTY.getType().equals(thingModel.getType())); | ||
| 93 | + // 1.2 解析 DB 里的字段 | ||
| 94 | + List<TDengineTableField> oldFields = new ArrayList<>(); | ||
| 95 | + try { | ||
| 96 | + oldFields.addAll(devicePropertyDataMapper.getProductPropertySTableFieldList(product.getProductKey())); | ||
| 97 | + } catch (Exception e) { | ||
| 98 | + if (!e.getMessage().contains("Table does not exist")) { | ||
| 99 | + throw e; | ||
| 100 | + } | ||
| 101 | + } | ||
| 102 | + | ||
| 103 | + // 2.1 情况一:如果是新增的时候,需要创建表 | ||
| 104 | + List<TDengineTableField> newFields = buildTableFieldList(thingModels); | ||
| 105 | + if (CollUtil.isEmpty(oldFields)) { | ||
| 106 | + if (CollUtil.isEmpty(newFields)) { | ||
| 107 | + log.info("[defineDevicePropertyData][productId({}) 没有需要定义的属性]", productId); | ||
| 108 | + return; | ||
| 109 | + } | ||
| 110 | + newFields.add(0, new TDengineTableField(TDengineTableField.FIELD_TS, TDengineTableField.TYPE_TIMESTAMP)); | ||
| 111 | + devicePropertyDataMapper.createProductPropertySTable(product.getProductKey(), newFields); | ||
| 112 | + return; | ||
| 113 | + } | ||
| 114 | + // 2.2 情况二:如果是修改的时候,需要更新表 | ||
| 115 | + devicePropertyDataMapper.alterProductPropertySTable(product.getProductKey(), oldFields, newFields); | ||
| 116 | + } | ||
| 117 | + | ||
| 118 | + private List<TDengineTableField> buildTableFieldList(List<IotThingModelDO> thingModels) { | ||
| 119 | + return convertList(thingModels, thingModel -> { | ||
| 120 | + TDengineTableField field = new TDengineTableField( | ||
| 121 | + StrUtil.toUnderlineCase(thingModel.getIdentifier()), // TDengine 字段默认都是小写 | ||
| 122 | + TYPE_MAPPING.get(thingModel.getProperty().getDataType())); | ||
| 123 | + if (thingModel.getProperty().getDataType().equals(IotDataSpecsDataTypeEnum.TEXT.getDataType())) { | ||
| 124 | + field.setLength(((ThingModelDateOrTextDataSpecs) thingModel.getProperty().getDataSpecs()).getLength()); | ||
| 125 | + } | ||
| 126 | + return field; | ||
| 127 | + }); | ||
| 128 | + } | ||
| 129 | + | ||
| 130 | + @Override | ||
| 131 | + public void saveDeviceData(String productKey, String deviceName, String message) { | ||
| 132 | + // 1. 根据产品 key 和设备名称,获得设备信息 | ||
| 133 | + IotDeviceDO device = deviceService.getDeviceByProductKeyAndDeviceName(productKey, deviceName); | ||
| 134 | + // 2. 解析消息,保存数据 | ||
| 135 | + JSONObject jsonObject = new JSONObject(message); | ||
| 136 | + log.info("[saveDeviceData][productKey({}) deviceName({}) data({})]", productKey, deviceName, jsonObject); | ||
| 137 | + ThingModelMessage thingModelMessage = ThingModelMessage.builder() | ||
| 138 | + .id(jsonObject.getStr("id")) | ||
| 139 | + .sys(jsonObject.get("sys")) | ||
| 140 | + .method(jsonObject.getStr("method")) | ||
| 141 | + .params(jsonObject.get("params")) | ||
| 142 | + .time(jsonObject.getLong("time") == null ? System.currentTimeMillis() : jsonObject.getLong("time")) | ||
| 143 | + .productKey(productKey) | ||
| 144 | + .deviceName(deviceName) | ||
| 145 | + .deviceKey(device.getDeviceKey()) | ||
| 146 | + .build(); | ||
| 147 | + thingModelMessageService.saveThingModelMessage(device, thingModelMessage); | ||
| 148 | + } | ||
| 149 | + | ||
| 150 | + @Override | ||
| 151 | + public List<IotDeviceDataDO> getLatestDeviceProperties(@Valid IotDeviceDataPageReqVO deviceDataReqVO) { | ||
| 152 | + List<IotDeviceDataDO> list = new ArrayList<>(); | ||
| 153 | + // 1. 获取设备信息 | ||
| 154 | + IotDeviceDO device = deviceService.getDevice(deviceDataReqVO.getDeviceId()); | ||
| 155 | + // 2. 获取设备属性最新数据 | ||
| 156 | + List<IotThingModelDO> thingModelList = thingModelService.getProductThingModelListByProductKey(device.getProductKey()); | ||
| 157 | + thingModelList = filterList(thingModelList, thingModel -> IotThingModelTypeEnum.PROPERTY.getType() | ||
| 158 | + .equals(thingModel.getType())); | ||
| 159 | + | ||
| 160 | + // 3. 过滤标识符和属性名称 | ||
| 161 | + if (deviceDataReqVO.getIdentifier() != null) { | ||
| 162 | + thingModelList = filterList(thingModelList, thingModel -> thingModel.getIdentifier() | ||
| 163 | + .toLowerCase().contains(deviceDataReqVO.getIdentifier().toLowerCase())); | ||
| 164 | + } | ||
| 165 | + if (deviceDataReqVO.getName() != null) { | ||
| 166 | + thingModelList = filterList(thingModelList, thingModel -> thingModel.getName() | ||
| 167 | + .toLowerCase().contains(deviceDataReqVO.getName().toLowerCase())); | ||
| 168 | + } | ||
| 169 | + // 4. 获取设备属性最新数据 | ||
| 170 | + thingModelList.forEach(thingModel -> { | ||
| 171 | + IotDeviceDataDO deviceData = deviceDataRedisDAO.get(device.getProductKey(), device.getDeviceName(), thingModel.getIdentifier()); | ||
| 172 | + if (deviceData == null) { | ||
| 173 | + deviceData = new IotDeviceDataDO(); | ||
| 174 | + deviceData.setProductKey(device.getProductKey()); | ||
| 175 | + deviceData.setDeviceName(device.getDeviceName()); | ||
| 176 | + deviceData.setIdentifier(thingModel.getIdentifier()); | ||
| 177 | + deviceData.setDeviceId(deviceDataReqVO.getDeviceId()); | ||
| 178 | + deviceData.setThingModelId(thingModel.getId()); | ||
| 179 | + deviceData.setName(thingModel.getName()); | ||
| 180 | + deviceData.setDataType(thingModel.getProperty().getDataType()); | ||
| 181 | + } | ||
| 182 | + list.add(deviceData); | ||
| 183 | + }); | ||
| 184 | + return list; | ||
| 185 | + } | ||
| 186 | + | ||
| 187 | + @Override | ||
| 188 | + public PageResult<Map<String, Object>> getHistoryDeviceProperties(IotDeviceDataPageReqVO deviceDataReqVO) { | ||
| 189 | + PageResult<Map<String, Object>> pageResult = new PageResult<>(); | ||
| 190 | + // 1. 获取设备信息 | ||
| 191 | + IotDeviceDO device = deviceService.getDevice(deviceDataReqVO.getDeviceId()); | ||
| 192 | + // 2. 获取设备属性历史数据 | ||
| 193 | + SelectVisualDO selectVisualDO = new SelectVisualDO(); | ||
| 194 | + selectVisualDO.setDataBaseName(getDatabaseName()); | ||
| 195 | + selectVisualDO.setTableName(getDeviceTableName(device.getProductKey(), device.getDeviceName())); | ||
| 196 | + selectVisualDO.setFieldName(deviceDataReqVO.getIdentifier()); | ||
| 197 | + selectVisualDO.setStartTime(DateUtil.date(deviceDataReqVO.getTimes()[0].atZone(ZoneId.systemDefault()).toInstant().toEpochMilli()).getTime()); | ||
| 198 | + selectVisualDO.setEndTime(DateUtil.date(deviceDataReqVO.getTimes()[1].atZone(ZoneId.systemDefault()).toInstant().toEpochMilli()).getTime()); | ||
| 199 | + Map<String, Object> params = new HashMap<>(); | ||
| 200 | + params.put("rows", deviceDataReqVO.getPageSize()); | ||
| 201 | + params.put("page", (deviceDataReqVO.getPageNo() - 1) * deviceDataReqVO.getPageSize()); | ||
| 202 | + selectVisualDO.setParams(params); | ||
| 203 | + pageResult.setList(tdEngineDMLMapper.selectHistoryDataList(selectVisualDO)); | ||
| 204 | + pageResult.setTotal(tdEngineDMLMapper.selectHistoryCount(selectVisualDO)); | ||
| 205 | + return pageResult; | ||
| 206 | + } | ||
| 207 | + | ||
| 208 | + private String getDatabaseName() { | ||
| 209 | + return StrUtil.subAfter(url, "/", true); | ||
| 210 | + } | ||
| 211 | + | ||
| 212 | + private static String getDeviceTableName(String productKey, String deviceName) { | ||
| 213 | + return String.format(IotConstants.DEVICE_TABLE_NAME_FORMAT, productKey, deviceName); | ||
| 214 | + } | ||
| 215 | + | ||
| 216 | +} | ||
| 0 | \ No newline at end of file | 217 | \ No newline at end of file |
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; | @@ -9,6 +9,7 @@ import cn.iocoder.yudao.framework.common.pojo.PageResult; | ||
| 9 | import cn.iocoder.yudao.framework.common.util.object.BeanUtils; | 9 | import cn.iocoder.yudao.framework.common.util.object.BeanUtils; |
| 10 | import cn.iocoder.yudao.framework.common.util.validation.ValidationUtils; | 10 | import cn.iocoder.yudao.framework.common.util.validation.ValidationUtils; |
| 11 | import cn.iocoder.yudao.framework.tenant.core.aop.TenantIgnore; | 11 | import cn.iocoder.yudao.framework.tenant.core.aop.TenantIgnore; |
| 12 | +import cn.iocoder.yudao.framework.tenant.core.util.TenantUtils; | ||
| 12 | import cn.iocoder.yudao.module.iot.controller.admin.device.vo.device.*; | 13 | import cn.iocoder.yudao.module.iot.controller.admin.device.vo.device.*; |
| 13 | import cn.iocoder.yudao.module.iot.dal.dataobject.device.IotDeviceDO; | 14 | import cn.iocoder.yudao.module.iot.dal.dataobject.device.IotDeviceDO; |
| 14 | import cn.iocoder.yudao.module.iot.dal.dataobject.device.IotDeviceGroupDO; | 15 | import cn.iocoder.yudao.module.iot.dal.dataobject.device.IotDeviceGroupDO; |
| @@ -60,9 +61,11 @@ public class IotDeviceServiceImpl implements IotDeviceService { | @@ -60,9 +61,11 @@ public class IotDeviceServiceImpl implements IotDeviceService { | ||
| 60 | throw exception(PRODUCT_NOT_EXISTS); | 61 | throw exception(PRODUCT_NOT_EXISTS); |
| 61 | } | 62 | } |
| 62 | // 1.2 校验设备标识是否唯一 | 63 | // 1.2 校验设备标识是否唯一 |
| 63 | - if (deviceMapper.selectByDeviceKey(createReqVO.getDeviceKey()) != null) { | ||
| 64 | - throw exception(DEVICE_KEY_EXISTS); | ||
| 65 | - } | 64 | + TenantUtils.executeIgnore(() -> { |
| 65 | + if (deviceMapper.selectByDeviceKey(createReqVO.getDeviceKey()) != null) { | ||
| 66 | + throw exception(PRODUCT_KEY_EXISTS); | ||
| 67 | + } | ||
| 68 | + }); | ||
| 66 | // 1.3 校验设备名称在同一产品下是否唯一 | 69 | // 1.3 校验设备名称在同一产品下是否唯一 |
| 67 | if (deviceMapper.selectByProductKeyAndDeviceName(product.getProductKey(), createReqVO.getDeviceKey()) != null) { | 70 | if (deviceMapper.selectByProductKeyAndDeviceName(product.getProductKey(), createReqVO.getDeviceKey()) != null) { |
| 68 | throw exception(DEVICE_NAME_EXISTS); | 71 | throw exception(DEVICE_NAME_EXISTS); |
yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/plugininfo/PluginInfoService.java renamed to yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/plugin/PluginInfoService.java
| 1 | -package cn.iocoder.yudao.module.iot.service.plugininfo; | 1 | +package cn.iocoder.yudao.module.iot.service.plugin; |
| 2 | 2 | ||
| 3 | import cn.iocoder.yudao.framework.common.pojo.PageResult; | 3 | import cn.iocoder.yudao.framework.common.pojo.PageResult; |
| 4 | -import cn.iocoder.yudao.module.iot.controller.admin.plugininfo.vo.PluginInfoPageReqVO; | ||
| 5 | -import cn.iocoder.yudao.module.iot.controller.admin.plugininfo.vo.PluginInfoSaveReqVO; | 4 | +import cn.iocoder.yudao.module.iot.controller.admin.plugin.vo.PluginInfoPageReqVO; |
| 5 | +import cn.iocoder.yudao.module.iot.controller.admin.plugin.vo.PluginInfoSaveReqVO; | ||
| 6 | import cn.iocoder.yudao.module.iot.dal.dataobject.plugininfo.PluginInfoDO; | 6 | import cn.iocoder.yudao.module.iot.dal.dataobject.plugininfo.PluginInfoDO; |
| 7 | import jakarta.validation.Valid; | 7 | import jakarta.validation.Valid; |
| 8 | import org.springframework.web.multipart.MultipartFile; | 8 | import org.springframework.web.multipart.MultipartFile; |
| 9 | 9 | ||
| 10 | +import java.util.List; | ||
| 11 | + | ||
| 10 | /** | 12 | /** |
| 11 | * IoT 插件信息 Service 接口 | 13 | * IoT 插件信息 Service 接口 |
| 12 | * | 14 | * |
| @@ -58,7 +60,7 @@ public interface PluginInfoService { | @@ -58,7 +60,7 @@ public interface PluginInfoService { | ||
| 58 | * @param id 插件id | 60 | * @param id 插件id |
| 59 | * @param file 文件 | 61 | * @param file 文件 |
| 60 | */ | 62 | */ |
| 61 | - void uploadJar(Long id, MultipartFile file); | 63 | + void uploadFile(Long id, MultipartFile file); |
| 62 | 64 | ||
| 63 | /** | 65 | /** |
| 64 | * 更新插件的状态 | 66 | * 更新插件的状态 |
| @@ -67,4 +69,11 @@ public interface PluginInfoService { | @@ -67,4 +69,11 @@ public interface PluginInfoService { | ||
| 67 | * @param status 状态 | 69 | * @param status 状态 |
| 68 | */ | 70 | */ |
| 69 | void updatePluginStatus(Long id, Integer status); | 71 | void updatePluginStatus(Long id, Integer status); |
| 72 | + | ||
| 73 | + /** | ||
| 74 | + * 获得插件信息列表 | ||
| 75 | + * | ||
| 76 | + * @return 插件信息列表 | ||
| 77 | + */ | ||
| 78 | + List<PluginInfoDO> getPluginInfoList(); | ||
| 70 | } | 79 | } |
| 71 | \ No newline at end of file | 80 | \ No newline at end of file |
yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/plugininfo/PluginInfoServiceImpl.java renamed to yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/plugin/PluginInfoServiceImpl.java
| 1 | -package cn.iocoder.yudao.module.iot.service.plugininfo; | 1 | +package cn.iocoder.yudao.module.iot.service.plugin; |
| 2 | 2 | ||
| 3 | -import cn.hutool.core.io.IoUtil; | ||
| 4 | import cn.iocoder.yudao.framework.common.pojo.PageResult; | 3 | import cn.iocoder.yudao.framework.common.pojo.PageResult; |
| 5 | import cn.iocoder.yudao.framework.common.util.object.BeanUtils; | 4 | import cn.iocoder.yudao.framework.common.util.object.BeanUtils; |
| 6 | -import cn.iocoder.yudao.module.infra.api.file.FileApi; | ||
| 7 | -import cn.iocoder.yudao.module.iot.controller.admin.plugininfo.vo.PluginInfoPageReqVO; | ||
| 8 | -import cn.iocoder.yudao.module.iot.controller.admin.plugininfo.vo.PluginInfoSaveReqVO; | 5 | +import cn.iocoder.yudao.module.iot.controller.admin.plugin.vo.PluginInfoPageReqVO; |
| 6 | +import cn.iocoder.yudao.module.iot.controller.admin.plugin.vo.PluginInfoSaveReqVO; | ||
| 9 | import cn.iocoder.yudao.module.iot.dal.dataobject.plugininfo.PluginInfoDO; | 7 | import cn.iocoder.yudao.module.iot.dal.dataobject.plugininfo.PluginInfoDO; |
| 10 | -import cn.iocoder.yudao.module.iot.dal.mysql.plugininfo.PluginInfoMapper; | 8 | +import cn.iocoder.yudao.module.iot.dal.mysql.plugin.PluginInfoMapper; |
| 11 | import cn.iocoder.yudao.module.iot.enums.plugin.IotPluginStatusEnum; | 9 | import cn.iocoder.yudao.module.iot.enums.plugin.IotPluginStatusEnum; |
| 12 | -import jakarta.annotation.PostConstruct; | ||
| 13 | import jakarta.annotation.Resource; | 10 | import jakarta.annotation.Resource; |
| 14 | -import lombok.SneakyThrows; | ||
| 15 | import lombok.extern.slf4j.Slf4j; | 11 | import lombok.extern.slf4j.Slf4j; |
| 16 | import org.pf4j.PluginDescriptor; | 12 | import org.pf4j.PluginDescriptor; |
| 17 | import org.pf4j.PluginState; | 13 | import org.pf4j.PluginState; |
| @@ -22,11 +18,13 @@ import org.springframework.stereotype.Service; | @@ -22,11 +18,13 @@ import org.springframework.stereotype.Service; | ||
| 22 | import org.springframework.validation.annotation.Validated; | 18 | import org.springframework.validation.annotation.Validated; |
| 23 | import org.springframework.web.multipart.MultipartFile; | 19 | import org.springframework.web.multipart.MultipartFile; |
| 24 | 20 | ||
| 25 | -import java.nio.file.Path; | 21 | +import java.io.File; |
| 22 | +import java.io.IOException; | ||
| 23 | +import java.nio.file.*; | ||
| 24 | +import java.util.ArrayList; | ||
| 25 | +import java.util.List; | ||
| 26 | import java.util.concurrent.Executors; | 26 | import java.util.concurrent.Executors; |
| 27 | import java.util.concurrent.TimeUnit; | 27 | import java.util.concurrent.TimeUnit; |
| 28 | -import java.util.jar.JarEntry; | ||
| 29 | -import java.util.jar.JarFile; | ||
| 30 | 28 | ||
| 31 | import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception; | 29 | import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception; |
| 32 | import static cn.iocoder.yudao.module.iot.enums.ErrorCodeConstants.*; | 30 | import static cn.iocoder.yudao.module.iot.enums.ErrorCodeConstants.*; |
| @@ -43,13 +41,9 @@ public class PluginInfoServiceImpl implements PluginInfoService { | @@ -43,13 +41,9 @@ public class PluginInfoServiceImpl implements PluginInfoService { | ||
| 43 | 41 | ||
| 44 | @Resource | 42 | @Resource |
| 45 | private PluginInfoMapper pluginInfoMapper; | 43 | private PluginInfoMapper pluginInfoMapper; |
| 46 | - | ||
| 47 | @Resource | 44 | @Resource |
| 48 | private SpringPluginManager pluginManager; | 45 | private SpringPluginManager pluginManager; |
| 49 | 46 | ||
| 50 | - @Resource | ||
| 51 | - private FileApi fileApi; | ||
| 52 | - | ||
| 53 | @Value("${pf4j.pluginsDir}") | 47 | @Value("${pf4j.pluginsDir}") |
| 54 | private String pluginsDir; | 48 | private String pluginsDir; |
| 55 | 49 | ||
| @@ -82,7 +76,7 @@ public class PluginInfoServiceImpl implements PluginInfoService { | @@ -82,7 +76,7 @@ public class PluginInfoServiceImpl implements PluginInfoService { | ||
| 82 | } | 76 | } |
| 83 | 77 | ||
| 84 | // 卸载插件 | 78 | // 卸载插件 |
| 85 | - PluginWrapper plugin = pluginManager.getPlugin(pluginInfoDO.getPluginId()); | 79 | + PluginWrapper plugin = pluginManager.getPlugin(pluginInfoDO.getPluginKey()); |
| 86 | if (plugin != null) { | 80 | if (plugin != null) { |
| 87 | // 查询插件是否是启动状态 | 81 | // 查询插件是否是启动状态 |
| 88 | if (plugin.getPluginState().equals(PluginState.STARTED)) { | 82 | if (plugin.getPluginState().equals(PluginState.STARTED)) { |
| @@ -95,6 +89,19 @@ public class PluginInfoServiceImpl implements PluginInfoService { | @@ -95,6 +89,19 @@ public class PluginInfoServiceImpl implements PluginInfoService { | ||
| 95 | 89 | ||
| 96 | // 删除 | 90 | // 删除 |
| 97 | pluginInfoMapper.deleteById(id); | 91 | pluginInfoMapper.deleteById(id); |
| 92 | + // 删除插件文件 | ||
| 93 | + Executors.newSingleThreadExecutor().submit(() -> { | ||
| 94 | + try { | ||
| 95 | + TimeUnit.SECONDS.sleep(1); // 等待 1 秒,避免插件未卸载完毕 | ||
| 96 | + File file = new File(pluginsDir, pluginInfoDO.getFileName()); | ||
| 97 | + if (file.exists() && !file.delete()) { | ||
| 98 | + log.error("[deletePluginInfo][删除插件文件({}) 失败]", pluginInfoDO.getFileName()); | ||
| 99 | + } | ||
| 100 | + } catch (InterruptedException e) { | ||
| 101 | + log.error("[deletePluginInfo][删除插件文件({}) 失败]", pluginInfoDO.getFileName(), e); | ||
| 102 | + } | ||
| 103 | + }); | ||
| 104 | + | ||
| 98 | } | 105 | } |
| 99 | 106 | ||
| 100 | private PluginInfoDO validatePluginInfoExists(Long id) { | 107 | private PluginInfoDO validatePluginInfoExists(Long id) { |
| @@ -116,73 +123,100 @@ public class PluginInfoServiceImpl implements PluginInfoService { | @@ -116,73 +123,100 @@ public class PluginInfoServiceImpl implements PluginInfoService { | ||
| 116 | } | 123 | } |
| 117 | 124 | ||
| 118 | @Override | 125 | @Override |
| 119 | - public void uploadJar(Long id, MultipartFile file) { | ||
| 120 | - // 1. 校验存在 | 126 | + public void uploadFile(Long id, MultipartFile file) { |
| 127 | + // 1. 校验插件信息是否存在 | ||
| 121 | PluginInfoDO pluginInfoDo = validatePluginInfoExists(id); | 128 | PluginInfoDO pluginInfoDo = validatePluginInfoExists(id); |
| 122 | 129 | ||
| 123 | - // 2. 判断文件名称与插件 ID 是否匹配 | ||
| 124 | - String pluginId = pluginInfoDo.getPluginId(); | 130 | + // 2. 获取插件标识 |
| 131 | + String pluginKey = pluginInfoDo.getPluginKey(); | ||
| 132 | + | ||
| 133 | + // 3. 停止并卸载旧的插件 | ||
| 134 | + stopAndUnloadPlugin(pluginKey); | ||
| 135 | + | ||
| 136 | + // 4. 上传新的插件文件 | ||
| 137 | + String pluginKeyNew = uploadAndLoadNewPlugin(file); | ||
| 125 | 138 | ||
| 126 | - // 3. 停止卸载旧的插件 | ||
| 127 | - // 3.1. 获取插件信息 | ||
| 128 | - PluginWrapper plugin = pluginManager.getPlugin(pluginId); | 139 | + // 5. 更新插件启用状态文件 |
| 140 | + updatePluginStatusFile(pluginKeyNew, false); | ||
| 141 | + | ||
| 142 | + // 6. 更新插件信息 | ||
| 143 | + updatePluginInfo(pluginInfoDo, pluginKeyNew, file); | ||
| 144 | + } | ||
| 145 | + | ||
| 146 | + // 停止并卸载旧的插件 | ||
| 147 | + private void stopAndUnloadPlugin(String pluginKey) { | ||
| 148 | + PluginWrapper plugin = pluginManager.getPlugin(pluginKey); | ||
| 129 | if (plugin != null) { | 149 | if (plugin != null) { |
| 130 | - // 3.2. 如果插件状态是启动的,停止插件 | ||
| 131 | if (plugin.getPluginState().equals(PluginState.STARTED)) { | 150 | if (plugin.getPluginState().equals(PluginState.STARTED)) { |
| 132 | - pluginManager.stopPlugin(pluginId); | 151 | + pluginManager.stopPlugin(pluginKey); // 停止插件 |
| 133 | } | 152 | } |
| 134 | - // 3.3. 卸载插件 | ||
| 135 | - pluginManager.unloadPlugin(pluginId); | 153 | + pluginManager.unloadPlugin(pluginKey); // 卸载插件 |
| 136 | } | 154 | } |
| 155 | + } | ||
| 137 | 156 | ||
| 138 | - // 4. 上传插件 | ||
| 139 | - String pluginIdNew; | 157 | + // 上传并加载新的插件文件 |
| 158 | + private String uploadAndLoadNewPlugin(MultipartFile file) { | ||
| 159 | + Path pluginsPath = Paths.get(pluginsDir); | ||
| 140 | try { | 160 | try { |
| 141 | - String path = fileApi.createFile(pluginsDir, IoUtil.readBytes(file.getInputStream())); | ||
| 142 | - Path pluginPath = Path.of(path); | ||
| 143 | - pluginIdNew = pluginManager.loadPlugin(pluginPath); | 161 | + if (!Files.exists(pluginsPath)) { |
| 162 | + Files.createDirectories(pluginsPath); // 创建插件目录 | ||
| 163 | + } | ||
| 164 | + String filename = file.getOriginalFilename(); | ||
| 165 | + if (filename != null) { | ||
| 166 | + Path jarPath = pluginsPath.resolve(filename); | ||
| 167 | + Files.copy(file.getInputStream(), jarPath, StandardCopyOption.REPLACE_EXISTING); // 保存上传的 JAR 文件 | ||
| 168 | + return pluginManager.loadPlugin(jarPath.toAbsolutePath()); // 加载插件 | ||
| 169 | + } else { | ||
| 170 | + throw exception(PLUGIN_INSTALL_FAILED); | ||
| 171 | + } | ||
| 144 | } catch (Exception e) { | 172 | } catch (Exception e) { |
| 145 | throw exception(PLUGIN_INSTALL_FAILED); | 173 | throw exception(PLUGIN_INSTALL_FAILED); |
| 146 | } | 174 | } |
| 175 | + } | ||
| 147 | 176 | ||
| 148 | - PluginWrapper pluginWrapper = pluginManager.getPlugin(pluginIdNew); | ||
| 149 | - if (pluginWrapper == null) { | ||
| 150 | - throw exception(PLUGIN_INSTALL_FAILED); | ||
| 151 | - } | 177 | + // 更新插件状态文件 |
| 178 | + private void updatePluginStatusFile(String pluginKeyNew, boolean isEnabled) { | ||
| 179 | + Path enabledFilePath = Paths.get(pluginsDir, "enabled.txt"); | ||
| 180 | + Path disabledFilePath = Paths.get(pluginsDir, "disabled.txt"); | ||
| 181 | + Path targetFilePath = isEnabled ? enabledFilePath : disabledFilePath; | ||
| 182 | + Path oppositeFilePath = isEnabled ? disabledFilePath : enabledFilePath; | ||
| 152 | 183 | ||
| 153 | - // 5. 读取配置文件和脚本 | ||
| 154 | - String configJson = ""; | ||
| 155 | - String script = ""; | ||
| 156 | - try (JarFile jarFile = new JarFile(pluginWrapper.getPluginPath().toFile())) { | ||
| 157 | - // 5.1 获取config文件在jar包中的路径 | ||
| 158 | - String configFile = "classes/config.json"; | ||
| 159 | - JarEntry configEntry = jarFile.getJarEntry(configFile); | ||
| 160 | - | ||
| 161 | - if (configEntry != null) { | ||
| 162 | - // 5.2 读取配置文件 | ||
| 163 | - configJson = IoUtil.readUtf8(jarFile.getInputStream(configEntry)); | ||
| 164 | - log.info("configJson:{}", configJson); | 184 | + try { |
| 185 | + PluginWrapper pluginWrapper = pluginManager.getPlugin(pluginKeyNew); | ||
| 186 | + if (pluginWrapper == null) { | ||
| 187 | + throw exception(PLUGIN_INSTALL_FAILED); | ||
| 188 | + } | ||
| 189 | + String pluginInfo = pluginKeyNew + "@" + pluginWrapper.getDescriptor().getVersion(); | ||
| 190 | + List<String> targetLines = Files.exists(targetFilePath) ? Files.readAllLines(targetFilePath) | ||
| 191 | + : new ArrayList<>(); | ||
| 192 | + List<String> oppositeLines = Files.exists(oppositeFilePath) ? Files.readAllLines(oppositeFilePath) | ||
| 193 | + : new ArrayList<>(); | ||
| 194 | + | ||
| 195 | + if (!targetLines.contains(pluginInfo)) { | ||
| 196 | + targetLines.add(pluginInfo); | ||
| 197 | + Files.write(targetFilePath, targetLines, StandardOpenOption.CREATE, | ||
| 198 | + StandardOpenOption.TRUNCATE_EXISTING); | ||
| 165 | } | 199 | } |
| 166 | 200 | ||
| 167 | - // 5.3 读取script.js脚本 | ||
| 168 | - String scriptFile = "classes/script.js"; | ||
| 169 | - JarEntry scriptEntity = jarFile.getJarEntry(scriptFile); | ||
| 170 | - if (scriptEntity != null) { | ||
| 171 | - // 5.4 读取脚本文件 | ||
| 172 | - script = IoUtil.readUtf8(jarFile.getInputStream(scriptEntity)); | ||
| 173 | - log.info("script:{}", script); | 201 | + if (oppositeLines.contains(pluginInfo)) { |
| 202 | + oppositeLines.remove(pluginInfo); | ||
| 203 | + Files.write(oppositeFilePath, oppositeLines, StandardOpenOption.CREATE, | ||
| 204 | + StandardOpenOption.TRUNCATE_EXISTING); | ||
| 174 | } | 205 | } |
| 175 | - } catch (Exception e) { | 206 | + } catch (IOException e) { |
| 176 | throw exception(PLUGIN_INSTALL_FAILED); | 207 | throw exception(PLUGIN_INSTALL_FAILED); |
| 177 | } | 208 | } |
| 209 | + } | ||
| 178 | 210 | ||
| 179 | - pluginInfoDo.setPluginId(pluginIdNew); | 211 | + // 更新插件信息 |
| 212 | + private void updatePluginInfo(PluginInfoDO pluginInfoDo, String pluginKeyNew, MultipartFile file) { | ||
| 213 | + pluginInfoDo.setPluginKey(pluginKeyNew); | ||
| 180 | pluginInfoDo.setStatus(IotPluginStatusEnum.STOPPED.getStatus()); | 214 | pluginInfoDo.setStatus(IotPluginStatusEnum.STOPPED.getStatus()); |
| 181 | - pluginInfoDo.setFile(file.getOriginalFilename()); | ||
| 182 | - pluginInfoDo.setConfigSchema(configJson); | ||
| 183 | - pluginInfoDo.setScript(script); | 215 | + pluginInfoDo.setFileName(file.getOriginalFilename()); |
| 216 | + pluginInfoDo.setScript(""); | ||
| 184 | 217 | ||
| 185 | - PluginDescriptor pluginDescriptor = pluginWrapper.getDescriptor(); | 218 | + PluginDescriptor pluginDescriptor = pluginManager.getPlugin(pluginKeyNew).getDescriptor(); |
| 219 | + pluginInfoDo.setConfigSchema(pluginDescriptor.getPluginDescription()); | ||
| 186 | pluginInfoDo.setVersion(pluginDescriptor.getVersion()); | 220 | pluginInfoDo.setVersion(pluginDescriptor.getVersion()); |
| 187 | pluginInfoDo.setDescription(pluginDescriptor.getPluginDescription()); | 221 | pluginInfoDo.setDescription(pluginDescriptor.getPluginDescription()); |
| 188 | pluginInfoMapper.updateById(pluginInfoDo); | 222 | pluginInfoMapper.updateById(pluginInfoDo); |
| @@ -190,52 +224,47 @@ public class PluginInfoServiceImpl implements PluginInfoService { | @@ -190,52 +224,47 @@ public class PluginInfoServiceImpl implements PluginInfoService { | ||
| 190 | 224 | ||
| 191 | @Override | 225 | @Override |
| 192 | public void updatePluginStatus(Long id, Integer status) { | 226 | public void updatePluginStatus(Long id, Integer status) { |
| 193 | - // 1. 校验存在 | 227 | + // 1. 校验插件信息是否存在 |
| 194 | PluginInfoDO pluginInfoDo = validatePluginInfoExists(id); | 228 | PluginInfoDO pluginInfoDo = validatePluginInfoExists(id); |
| 195 | 229 | ||
| 196 | - // 插件状态无效 | 230 | + // 2. 校验插件状态是否有效 |
| 197 | if (!IotPluginStatusEnum.contains(status)) { | 231 | if (!IotPluginStatusEnum.contains(status)) { |
| 198 | throw exception(PLUGIN_STATUS_INVALID); | 232 | throw exception(PLUGIN_STATUS_INVALID); |
| 199 | } | 233 | } |
| 200 | 234 | ||
| 201 | - String pluginId = pluginInfoDo.getPluginId(); | ||
| 202 | - PluginWrapper plugin = pluginManager.getPlugin(pluginId); | 235 | + // 3. 获取插件标识和插件实例 |
| 236 | + String pluginKey = pluginInfoDo.getPluginKey(); | ||
| 237 | + PluginWrapper plugin = pluginManager.getPlugin(pluginKey); | ||
| 238 | + | ||
| 239 | + // 4. 根据状态更新插件 | ||
| 203 | if (plugin != null) { | 240 | if (plugin != null) { |
| 204 | - if (status.equals(IotPluginStatusEnum.RUNNING.getStatus()) && plugin.getPluginState() != PluginState.STARTED) { | ||
| 205 | - // 启动插件 | ||
| 206 | - pluginManager.startPlugin(pluginId); | ||
| 207 | - } else if (status.equals(IotPluginStatusEnum.STOPPED.getStatus()) && plugin.getPluginState() == PluginState.STARTED) { | ||
| 208 | - // 停止插件 | ||
| 209 | - pluginManager.stopPlugin(pluginId); | 241 | + // 4.1 如果目标状态是运行且插件未启动,则启动插件 |
| 242 | + if (status.equals(IotPluginStatusEnum.RUNNING.getStatus()) | ||
| 243 | + && plugin.getPluginState() != PluginState.STARTED) { | ||
| 244 | + pluginManager.startPlugin(pluginKey); | ||
| 245 | + updatePluginStatusFile(pluginKey, true); // 更新插件状态文件为启用 | ||
| 246 | + } | ||
| 247 | + // 4.2 如果目标状态是停止且插件已启动,则停止插件 | ||
| 248 | + else if (status.equals(IotPluginStatusEnum.STOPPED.getStatus()) | ||
| 249 | + && plugin.getPluginState() == PluginState.STARTED) { | ||
| 250 | + pluginManager.stopPlugin(pluginKey); | ||
| 251 | + updatePluginStatusFile(pluginKey, false); // 更新插件状态文件为禁用 | ||
| 210 | } | 252 | } |
| 211 | } else { | 253 | } else { |
| 212 | - // 已经停止,未获取到插件 | 254 | + // 5. 插件不存在且状态为停止,抛出异常 |
| 213 | if (IotPluginStatusEnum.STOPPED.getStatus().equals(pluginInfoDo.getStatus())) { | 255 | if (IotPluginStatusEnum.STOPPED.getStatus().equals(pluginInfoDo.getStatus())) { |
| 214 | throw exception(PLUGIN_STATUS_INVALID); | 256 | throw exception(PLUGIN_STATUS_INVALID); |
| 215 | } | 257 | } |
| 216 | } | 258 | } |
| 259 | + | ||
| 260 | + // 6. 更新数据库中的插件状态 | ||
| 217 | pluginInfoDo.setStatus(status); | 261 | pluginInfoDo.setStatus(status); |
| 218 | pluginInfoMapper.updateById(pluginInfoDo); | 262 | pluginInfoMapper.updateById(pluginInfoDo); |
| 219 | } | 263 | } |
| 220 | 264 | ||
| 221 | -// @PostConstruct | ||
| 222 | -// public void init() { | ||
| 223 | -// Executors.newSingleThreadScheduledExecutor().schedule(this::startPlugins, 3, TimeUnit.SECONDS); | ||
| 224 | -// } | ||
| 225 | -// | ||
| 226 | -// @SneakyThrows | ||
| 227 | -// private void startPlugins() { | ||
| 228 | -// for (PluginInfoDO pluginInfoDO : pluginInfoMapper.selectList()) { | ||
| 229 | -// if (!IotPluginStatusEnum.RUNNING.getStatus().equals(pluginInfoDO.getStatus())) { | ||
| 230 | -// continue; | ||
| 231 | -// } | ||
| 232 | -// log.info("start plugin:{}", pluginInfoDO.getPluginId()); | ||
| 233 | -// try { | ||
| 234 | -// pluginManager.startPlugin(pluginInfoDO.getPluginId()); | ||
| 235 | -// } catch (Exception e) { | ||
| 236 | -// log.error("start plugin error", e); | ||
| 237 | -// } | ||
| 238 | -// } | ||
| 239 | -// } | 265 | + @Override |
| 266 | + public List<PluginInfoDO> getPluginInfoList() { | ||
| 267 | + return pluginInfoMapper.selectList(null); | ||
| 268 | + } | ||
| 240 | 269 | ||
| 241 | } | 270 | } |
| 242 | \ No newline at end of file | 271 | \ No newline at end of file |
yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/plugin/PluginInstanceService.java
0 → 100644
| 1 | +package cn.iocoder.yudao.module.iot.service.plugin; | ||
| 2 | + | ||
| 3 | +/** | ||
| 4 | + * IoT 插件实例 Service 接口 | ||
| 5 | + * | ||
| 6 | + * @author 芋道源码 | ||
| 7 | + */ | ||
| 8 | +public interface PluginInstanceService { | ||
| 9 | + | ||
| 10 | + /** | ||
| 11 | + * 更新IoT 插件实例 | ||
| 12 | + */ | ||
| 13 | + void updatePluginInstances(); | ||
| 14 | + | ||
| 15 | +} | ||
| 0 | \ No newline at end of file | 16 | \ No newline at end of file |
yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/plugin/PluginInstanceServiceImpl.java
0 → 100644
| 1 | +package cn.iocoder.yudao.module.iot.service.plugin; | ||
| 2 | + | ||
| 3 | +import cn.hutool.core.net.NetUtil; | ||
| 4 | +import cn.hutool.core.util.IdUtil; | ||
| 5 | +import cn.iocoder.yudao.module.iot.dal.dataobject.plugininfo.PluginInfoDO; | ||
| 6 | +import cn.iocoder.yudao.module.iot.dal.dataobject.plugininstance.PluginInstanceDO; | ||
| 7 | +import cn.iocoder.yudao.module.iot.dal.mysql.plugin.PluginInstanceMapper; | ||
| 8 | +import jakarta.annotation.Resource; | ||
| 9 | +import lombok.extern.slf4j.Slf4j; | ||
| 10 | +import org.pf4j.PluginWrapper; | ||
| 11 | +import org.pf4j.spring.SpringPluginManager; | ||
| 12 | +import org.springframework.beans.factory.annotation.Value; | ||
| 13 | +import org.springframework.stereotype.Service; | ||
| 14 | +import org.springframework.validation.annotation.Validated; | ||
| 15 | + | ||
| 16 | +import java.util.List; | ||
| 17 | + | ||
| 18 | +/** | ||
| 19 | + * IoT 插件实例 Service 实现类 | ||
| 20 | + * | ||
| 21 | + * @author 芋道源码 | ||
| 22 | + */ | ||
| 23 | +@Service | ||
| 24 | +@Validated | ||
| 25 | +@Slf4j | ||
| 26 | +public class PluginInstanceServiceImpl implements PluginInstanceService { | ||
| 27 | + | ||
| 28 | + /** | ||
| 29 | + * 主程序id | ||
| 30 | + */ | ||
| 31 | + public static final String MAIN_ID = IdUtil.fastSimpleUUID(); | ||
| 32 | + | ||
| 33 | + @Resource | ||
| 34 | + private PluginInfoService pluginInfoService; | ||
| 35 | + @Resource | ||
| 36 | + private PluginInstanceMapper pluginInstanceMapper; | ||
| 37 | + @Resource | ||
| 38 | + private SpringPluginManager pluginManager; | ||
| 39 | + | ||
| 40 | + @Value("${server.port:48080}") | ||
| 41 | + private int port; | ||
| 42 | + | ||
| 43 | + | ||
| 44 | + @Override | ||
| 45 | + public void updatePluginInstances() { | ||
| 46 | + // 1. 查询 pf4j 插件列表 | ||
| 47 | + List<PluginWrapper> plugins = pluginManager.getPlugins(); | ||
| 48 | + | ||
| 49 | + // 2. 查询插件信息列表 | ||
| 50 | + List<PluginInfoDO> pluginInfos = pluginInfoService.getPluginInfoList(); | ||
| 51 | + | ||
| 52 | + // 动态获取主程序的 IP 和端口 | ||
| 53 | + String mainIp = getLocalIpAddress(); | ||
| 54 | + | ||
| 55 | + // 3. 遍历插件列表,并保存为插件实例 | ||
| 56 | + for (PluginWrapper plugin : plugins) { | ||
| 57 | + String pluginKey = plugin.getPluginId(); | ||
| 58 | + PluginInfoDO pluginInfo = pluginInfos.stream() | ||
| 59 | + .filter(pluginInfoDO -> pluginInfoDO.getPluginKey().equals(pluginKey)) | ||
| 60 | + .findFirst() | ||
| 61 | + .orElse(null); | ||
| 62 | + | ||
| 63 | + // 4. 如果插件信息不存在,则跳过 | ||
| 64 | + if (pluginInfo == null) { | ||
| 65 | + continue; | ||
| 66 | + } | ||
| 67 | + | ||
| 68 | + // 5. 查询插件实例 | ||
| 69 | + PluginInstanceDO pluginInstance = pluginInstanceMapper.selectByMainIdAndPluginId(MAIN_ID, pluginInfo.getId()); | ||
| 70 | + | ||
| 71 | + // 6. 如果插件实例不存在,则创建 | ||
| 72 | + if (pluginInstance == null) { | ||
| 73 | + pluginInstance = new PluginInstanceDO(); | ||
| 74 | + pluginInstance.setPluginId(pluginInfo.getId()); | ||
| 75 | + pluginInstance.setMainId(MAIN_ID); | ||
| 76 | + pluginInstance.setIp(mainIp); | ||
| 77 | + pluginInstance.setPort(port); | ||
| 78 | + pluginInstance.setHeartbeatAt(System.currentTimeMillis()); | ||
| 79 | + pluginInstanceMapper.insert(pluginInstance); | ||
| 80 | + } else { | ||
| 81 | + // 7. 如果插件实例存在,则更新 | ||
| 82 | + pluginInstance.setHeartbeatAt(System.currentTimeMillis()); | ||
| 83 | + pluginInstanceMapper.updateById(pluginInstance); | ||
| 84 | + } | ||
| 85 | + } | ||
| 86 | + } | ||
| 87 | + | ||
| 88 | + private String getLocalIpAddress() { | ||
| 89 | + try { | ||
| 90 | + List<String> ipList = NetUtil.localIpv4s().stream() | ||
| 91 | + .filter(ip -> !ip.startsWith("0.0") && !ip.startsWith("127.") && !ip.startsWith("169.254") && !ip.startsWith("255.255.255.255")) | ||
| 92 | + .toList(); | ||
| 93 | + return ipList.isEmpty() ? "127.0.0.1" : ipList.get(0); | ||
| 94 | + } catch (Exception e) { | ||
| 95 | + log.error("获取本地IP地址失败", e); | ||
| 96 | + return "127.0.0.1"; // 默认值 | ||
| 97 | + } | ||
| 98 | + } | ||
| 99 | + | ||
| 100 | +} | ||
| 0 | \ No newline at end of file | 101 | \ No newline at end of file |
yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/plugininstance/PluginInstanceService.java deleted
| 1 | -package cn.iocoder.yudao.module.iot.service.plugininstance; | ||
| 2 | - | ||
| 3 | -import cn.iocoder.yudao.framework.common.pojo.PageResult; | ||
| 4 | -import cn.iocoder.yudao.module.iot.controller.admin.plugininstance.vo.PluginInstancePageReqVO; | ||
| 5 | -import cn.iocoder.yudao.module.iot.controller.admin.plugininstance.vo.PluginInstanceSaveReqVO; | ||
| 6 | -import cn.iocoder.yudao.module.iot.dal.dataobject.plugininstance.PluginInstanceDO; | ||
| 7 | -import jakarta.validation.Valid; | ||
| 8 | - | ||
| 9 | -/** | ||
| 10 | - * IoT 插件实例 Service 接口 | ||
| 11 | - * | ||
| 12 | - * @author 芋道源码 | ||
| 13 | - */ | ||
| 14 | -public interface PluginInstanceService { | ||
| 15 | - | ||
| 16 | - /** | ||
| 17 | - * 创建IoT 插件实例 | ||
| 18 | - * | ||
| 19 | - * @param createReqVO 创建信息 | ||
| 20 | - * @return 编号 | ||
| 21 | - */ | ||
| 22 | - Long createPluginInstance(@Valid PluginInstanceSaveReqVO createReqVO); | ||
| 23 | - | ||
| 24 | - /** | ||
| 25 | - * 更新IoT 插件实例 | ||
| 26 | - * | ||
| 27 | - * @param updateReqVO 更新信息 | ||
| 28 | - */ | ||
| 29 | - void updatePluginInstance(@Valid PluginInstanceSaveReqVO updateReqVO); | ||
| 30 | - | ||
| 31 | - /** | ||
| 32 | - * 删除IoT 插件实例 | ||
| 33 | - * | ||
| 34 | - * @param id 编号 | ||
| 35 | - */ | ||
| 36 | - void deletePluginInstance(Long id); | ||
| 37 | - | ||
| 38 | - /** | ||
| 39 | - * 获得IoT 插件实例 | ||
| 40 | - * | ||
| 41 | - * @param id 编号 | ||
| 42 | - * @return IoT 插件实例 | ||
| 43 | - */ | ||
| 44 | - PluginInstanceDO getPluginInstance(Long id); | ||
| 45 | - | ||
| 46 | - /** | ||
| 47 | - * 获得IoT 插件实例分页 | ||
| 48 | - * | ||
| 49 | - * @param pageReqVO 分页查询 | ||
| 50 | - * @return IoT 插件实例分页 | ||
| 51 | - */ | ||
| 52 | - PageResult<PluginInstanceDO> getPluginInstancePage(PluginInstancePageReqVO pageReqVO); | ||
| 53 | - | ||
| 54 | -} | ||
| 55 | \ No newline at end of file | 0 | \ No newline at end of file |
yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/plugininstance/PluginInstanceServiceImpl.java deleted
| 1 | -package cn.iocoder.yudao.module.iot.service.plugininstance; | ||
| 2 | - | ||
| 3 | -import cn.iocoder.yudao.framework.common.pojo.PageResult; | ||
| 4 | -import cn.iocoder.yudao.framework.common.util.object.BeanUtils; | ||
| 5 | -import cn.iocoder.yudao.module.iot.controller.admin.plugininstance.vo.PluginInstancePageReqVO; | ||
| 6 | -import cn.iocoder.yudao.module.iot.controller.admin.plugininstance.vo.PluginInstanceSaveReqVO; | ||
| 7 | -import cn.iocoder.yudao.module.iot.dal.dataobject.plugininstance.PluginInstanceDO; | ||
| 8 | -import cn.iocoder.yudao.module.iot.dal.mysql.plugininstance.PluginInstanceMapper; | ||
| 9 | -import jakarta.annotation.Resource; | ||
| 10 | -import org.springframework.stereotype.Service; | ||
| 11 | -import org.springframework.validation.annotation.Validated; | ||
| 12 | - | ||
| 13 | -import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception; | ||
| 14 | -import static cn.iocoder.yudao.module.iot.enums.ErrorCodeConstants.PLUGIN_INSTANCE_NOT_EXISTS; | ||
| 15 | - | ||
| 16 | -/** | ||
| 17 | - * IoT 插件实例 Service 实现类 | ||
| 18 | - * | ||
| 19 | - * @author 芋道源码 | ||
| 20 | - */ | ||
| 21 | -@Service | ||
| 22 | -@Validated | ||
| 23 | -public class PluginInstanceServiceImpl implements PluginInstanceService { | ||
| 24 | - | ||
| 25 | - @Resource | ||
| 26 | - private PluginInstanceMapper pluginInstanceMapper; | ||
| 27 | - | ||
| 28 | - @Override | ||
| 29 | - public Long createPluginInstance(PluginInstanceSaveReqVO createReqVO) { | ||
| 30 | - // 插入 | ||
| 31 | - PluginInstanceDO pluginInstance = BeanUtils.toBean(createReqVO, PluginInstanceDO.class); | ||
| 32 | - pluginInstanceMapper.insert(pluginInstance); | ||
| 33 | - // 返回 | ||
| 34 | - return pluginInstance.getId(); | ||
| 35 | - } | ||
| 36 | - | ||
| 37 | - @Override | ||
| 38 | - public void updatePluginInstance(PluginInstanceSaveReqVO updateReqVO) { | ||
| 39 | - // 校验存在 | ||
| 40 | - validatePluginInstanceExists(updateReqVO.getId()); | ||
| 41 | - // 更新 | ||
| 42 | - PluginInstanceDO updateObj = BeanUtils.toBean(updateReqVO, PluginInstanceDO.class); | ||
| 43 | - pluginInstanceMapper.updateById(updateObj); | ||
| 44 | - } | ||
| 45 | - | ||
| 46 | - @Override | ||
| 47 | - public void deletePluginInstance(Long id) { | ||
| 48 | - // 校验存在 | ||
| 49 | - validatePluginInstanceExists(id); | ||
| 50 | - // 删除 | ||
| 51 | - pluginInstanceMapper.deleteById(id); | ||
| 52 | - } | ||
| 53 | - | ||
| 54 | - private void validatePluginInstanceExists(Long id) { | ||
| 55 | - if (pluginInstanceMapper.selectById(id) == null) { | ||
| 56 | - throw exception(PLUGIN_INSTANCE_NOT_EXISTS); | ||
| 57 | - } | ||
| 58 | - } | ||
| 59 | - | ||
| 60 | - @Override | ||
| 61 | - public PluginInstanceDO getPluginInstance(Long id) { | ||
| 62 | - return pluginInstanceMapper.selectById(id); | ||
| 63 | - } | ||
| 64 | - | ||
| 65 | - @Override | ||
| 66 | - public PageResult<PluginInstanceDO> getPluginInstancePage(PluginInstancePageReqVO pageReqVO) { | ||
| 67 | - return pluginInstanceMapper.selectPage(pageReqVO); | ||
| 68 | - } | ||
| 69 | - | ||
| 70 | -} | ||
| 71 | \ No newline at end of file | 0 | \ No newline at end of file |
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; | @@ -2,13 +2,14 @@ package cn.iocoder.yudao.module.iot.service.product; | ||
| 2 | 2 | ||
| 3 | import cn.iocoder.yudao.framework.common.pojo.PageResult; | 3 | import cn.iocoder.yudao.framework.common.pojo.PageResult; |
| 4 | import cn.iocoder.yudao.framework.common.util.object.BeanUtils; | 4 | import cn.iocoder.yudao.framework.common.util.object.BeanUtils; |
| 5 | +import cn.iocoder.yudao.framework.tenant.core.util.TenantUtils; | ||
| 5 | import cn.iocoder.yudao.module.iot.controller.admin.product.vo.product.IotProductPageReqVO; | 6 | import cn.iocoder.yudao.module.iot.controller.admin.product.vo.product.IotProductPageReqVO; |
| 6 | import cn.iocoder.yudao.module.iot.controller.admin.product.vo.product.IotProductSaveReqVO; | 7 | import cn.iocoder.yudao.module.iot.controller.admin.product.vo.product.IotProductSaveReqVO; |
| 7 | import cn.iocoder.yudao.module.iot.dal.dataobject.product.IotProductDO; | 8 | import cn.iocoder.yudao.module.iot.dal.dataobject.product.IotProductDO; |
| 8 | import cn.iocoder.yudao.module.iot.dal.mysql.product.IotProductMapper; | 9 | import cn.iocoder.yudao.module.iot.dal.mysql.product.IotProductMapper; |
| 9 | import cn.iocoder.yudao.module.iot.enums.product.IotProductStatusEnum; | 10 | import cn.iocoder.yudao.module.iot.enums.product.IotProductStatusEnum; |
| 11 | +import cn.iocoder.yudao.module.iot.service.device.IotDevicePropertyDataService; | ||
| 10 | import cn.iocoder.yudao.module.iot.service.tdengine.IotThingModelMessageService; | 12 | import cn.iocoder.yudao.module.iot.service.tdengine.IotThingModelMessageService; |
| 11 | -import cn.iocoder.yudao.module.iot.service.thingmodel.IotProductThingModelService; | ||
| 12 | import com.baomidou.dynamic.datasource.annotation.DSTransactional; | 13 | import com.baomidou.dynamic.datasource.annotation.DSTransactional; |
| 13 | import jakarta.annotation.Resource; | 14 | import jakarta.annotation.Resource; |
| 14 | import org.springframework.context.annotation.Lazy; | 15 | import org.springframework.context.annotation.Lazy; |
| @@ -34,18 +35,22 @@ public class IotProductServiceImpl implements IotProductService { | @@ -34,18 +35,22 @@ public class IotProductServiceImpl implements IotProductService { | ||
| 34 | private IotProductMapper productMapper; | 35 | private IotProductMapper productMapper; |
| 35 | 36 | ||
| 36 | @Resource | 37 | @Resource |
| 37 | - @Lazy // 延迟加载,解决循环依赖 | ||
| 38 | - private IotProductThingModelService thingModelFunctionService; | ||
| 39 | - @Resource | ||
| 40 | @Lazy // 延迟加载,解决循环依赖 | 38 | @Lazy // 延迟加载,解决循环依赖 |
| 41 | private IotThingModelMessageService thingModelMessageService; | 39 | private IotThingModelMessageService thingModelMessageService; |
| 40 | + @Resource | ||
| 41 | + @Lazy // 延迟加载,解决循环依赖 | ||
| 42 | + private IotDevicePropertyDataService devicePropertyDataService; | ||
| 42 | 43 | ||
| 43 | @Override | 44 | @Override |
| 44 | public Long createProduct(IotProductSaveReqVO createReqVO) { | 45 | public Long createProduct(IotProductSaveReqVO createReqVO) { |
| 45 | - // 1. 生成 ProductKey | ||
| 46 | - if (productMapper.selectByProductKey(createReqVO.getProductKey()) != null) { | ||
| 47 | - throw exception(PRODUCT_KEY_EXISTS); | ||
| 48 | - } | 46 | + // 1. 校验 ProductKey |
| 47 | + TenantUtils.executeIgnore(() -> { | ||
| 48 | + // 为什么忽略租户?避免多个租户之间,productKey 重复,导致 TDengine 设备属性表重复 | ||
| 49 | + if (productMapper.selectByProductKey(createReqVO.getProductKey()) != null) { | ||
| 50 | + throw exception(PRODUCT_KEY_EXISTS); | ||
| 51 | + } | ||
| 52 | + }); | ||
| 53 | + | ||
| 49 | // 2. 插入 | 54 | // 2. 插入 |
| 50 | IotProductDO product = BeanUtils.toBean(createReqVO, IotProductDO.class) | 55 | IotProductDO product = BeanUtils.toBean(createReqVO, IotProductDO.class) |
| 51 | .setStatus(IotProductStatusEnum.UNPUBLISHED.getStatus()); | 56 | .setStatus(IotProductStatusEnum.UNPUBLISHED.getStatus()); |
| @@ -119,8 +124,8 @@ public class IotProductServiceImpl implements IotProductService { | @@ -119,8 +124,8 @@ public class IotProductServiceImpl implements IotProductService { | ||
| 119 | // 3. 产品是发布状态 | 124 | // 3. 产品是发布状态 |
| 120 | if (Objects.equals(status, IotProductStatusEnum.PUBLISHED.getStatus())) { | 125 | if (Objects.equals(status, IotProductStatusEnum.PUBLISHED.getStatus())) { |
| 121 | // 3.1 创建产品超级表数据模型 | 126 | // 3.1 创建产品超级表数据模型 |
| 122 | - thingModelFunctionService.createSuperTableDataModel(id); | ||
| 123 | - // 3.2 创建物模型日志超级表数据模型 | 127 | + devicePropertyDataService.defineDevicePropertyData(id); |
| 128 | + // 3.2 创建物模型日志超级表数据模型 TODO 待定:message 要不要分; | ||
| 124 | thingModelMessageService.createSuperTable(id); | 129 | thingModelMessageService.createSuperTable(id); |
| 125 | } | 130 | } |
| 126 | productMapper.updateById(updateObj); | 131 | productMapper.updateById(updateObj); |
yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/tdengine/IotSuperTableService.java deleted
| 1 | -package cn.iocoder.yudao.module.iot.service.tdengine; | ||
| 2 | - | ||
| 3 | - | ||
| 4 | -import cn.iocoder.yudao.module.iot.dal.dataobject.product.IotProductDO; | ||
| 5 | -import cn.iocoder.yudao.module.iot.dal.dataobject.thingmodel.IotProductThingModelDO; | ||
| 6 | - | ||
| 7 | -import java.util.List; | ||
| 8 | - | ||
| 9 | -/** | ||
| 10 | - * IoT 超级表服务,负责根据物模型创建和更新超级表,以及创建超级表的子表等操作。 | ||
| 11 | - */ | ||
| 12 | -public interface IotSuperTableService { | ||
| 13 | - | ||
| 14 | - /** | ||
| 15 | - * 创建超级表数据模型 | ||
| 16 | - */ | ||
| 17 | - void createSuperTableDataModel(IotProductDO product, List<IotProductThingModelDO> thingModelList); | ||
| 18 | - | ||
| 19 | -} | ||
| 20 | \ No newline at end of file | 0 | \ No newline at end of file |
yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/tdengine/IotSuperTableServiceImpl.java deleted
| 1 | -package cn.iocoder.yudao.module.iot.service.tdengine; | ||
| 2 | - | ||
| 3 | -import cn.hutool.core.bean.BeanUtil; | ||
| 4 | -import cn.hutool.core.collection.CollUtil; | ||
| 5 | -import cn.iocoder.yudao.module.iot.controller.admin.thingmodel.model.ThingModelProperty; | ||
| 6 | -import cn.iocoder.yudao.module.iot.controller.admin.thingmodel.model.ThingModelRespVO; | ||
| 7 | -import cn.iocoder.yudao.module.iot.dal.dataobject.product.IotProductDO; | ||
| 8 | -import cn.iocoder.yudao.module.iot.dal.dataobject.tdengine.FieldParser; | ||
| 9 | -import cn.iocoder.yudao.module.iot.dal.dataobject.tdengine.TdFieldDO; | ||
| 10 | -import cn.iocoder.yudao.module.iot.dal.dataobject.tdengine.TdTableDO; | ||
| 11 | -import cn.iocoder.yudao.module.iot.dal.dataobject.thingmodel.IotProductThingModelDO; | ||
| 12 | -import cn.iocoder.yudao.module.iot.dal.tdengine.TdEngineDDLMapper; | ||
| 13 | -import cn.iocoder.yudao.module.iot.enums.thingmodel.IotProductThingModelTypeEnum; | ||
| 14 | -import jakarta.annotation.Resource; | ||
| 15 | -import lombok.extern.slf4j.Slf4j; | ||
| 16 | -import org.springframework.beans.factory.annotation.Value; | ||
| 17 | -import org.springframework.stereotype.Service; | ||
| 18 | - | ||
| 19 | -import java.util.*; | ||
| 20 | -import java.util.stream.Collectors; | ||
| 21 | - | ||
| 22 | -/** | ||
| 23 | - * IoT 超级表服务实现类,负责根据物模型创建和更新超级表,以及创建超级表的子表等操作。 | ||
| 24 | - */ | ||
| 25 | -@Service | ||
| 26 | -@Slf4j | ||
| 27 | -public class IotSuperTableServiceImpl implements IotSuperTableService { | ||
| 28 | - | ||
| 29 | - @Resource | ||
| 30 | - private TdEngineDDLMapper tdEngineDDLMapper; | ||
| 31 | - | ||
| 32 | - @Value("${spring.datasource.dynamic.datasource.tdengine.url}") | ||
| 33 | - private String url; | ||
| 34 | - | ||
| 35 | - @Override | ||
| 36 | - public void createSuperTableDataModel(IotProductDO product, List<IotProductThingModelDO> thingModelList) { | ||
| 37 | - ThingModelRespVO thingModel = buildThingModel(product, thingModelList); | ||
| 38 | - | ||
| 39 | - if (thingModel.getModel() == null || CollUtil.isEmpty(thingModel.getModel().getProperties())) { | ||
| 40 | - log.warn("物模型属性列表为空,不创建超级表"); | ||
| 41 | - return; | ||
| 42 | - } | ||
| 43 | - | ||
| 44 | - String superTableName = getSuperTableName(product.getDeviceType(), product.getProductKey()); | ||
| 45 | - String databaseName = getDatabaseName(); | ||
| 46 | - | ||
| 47 | - List<Map<String, Object>> results = tdEngineDDLMapper.showSuperTables(new TdTableDO(databaseName, superTableName)); | ||
| 48 | - int tableExists = results == null || results.isEmpty() ? 0 : results.size(); | ||
| 49 | - if (tableExists > 0) { | ||
| 50 | - updateSuperTable(thingModel, product.getDeviceType()); | ||
| 51 | - } else { | ||
| 52 | - createSuperTable(thingModel, product.getDeviceType()); | ||
| 53 | - } | ||
| 54 | - } | ||
| 55 | - | ||
| 56 | - /** | ||
| 57 | - * 创建超级表 | ||
| 58 | - */ | ||
| 59 | - private void createSuperTable(ThingModelRespVO thingModel, Integer deviceType) { | ||
| 60 | - // 解析物模型,获取字段列表 | ||
| 61 | - List<TdFieldDO> schemaFields = new ArrayList<>(); | ||
| 62 | - schemaFields.add(TdFieldDO.builder() | ||
| 63 | - .fieldName("time") | ||
| 64 | - .dataType("TIMESTAMP") | ||
| 65 | - .build()); | ||
| 66 | - schemaFields.addAll(FieldParser.parse(thingModel)); | ||
| 67 | - | ||
| 68 | - // 设置超级表的标签 | ||
| 69 | - List<TdFieldDO> tagsFields = List.of( | ||
| 70 | - TdFieldDO.builder().fieldName("product_key").dataType("NCHAR").dataLength(64).build(), | ||
| 71 | - TdFieldDO.builder().fieldName("device_key").dataType("NCHAR").dataLength(64).build(), | ||
| 72 | - TdFieldDO.builder().fieldName("device_name").dataType("NCHAR").dataLength(64).build(), | ||
| 73 | - TdFieldDO.builder().fieldName("device_type").dataType("INT").build() | ||
| 74 | - ); | ||
| 75 | - | ||
| 76 | - // 获取超级表的名称和数据库名称 | ||
| 77 | - String superTableName = getSuperTableName(deviceType, thingModel.getProductKey()); | ||
| 78 | - String databaseName = getDatabaseName(); | ||
| 79 | - | ||
| 80 | - // 创建超级表 | ||
| 81 | - tdEngineDDLMapper.createSuperTable(new TdTableDO(databaseName, superTableName, schemaFields, tagsFields)); | ||
| 82 | - } | ||
| 83 | - | ||
| 84 | - /** | ||
| 85 | - * 更新超级表 | ||
| 86 | - */ | ||
| 87 | - private void updateSuperTable(ThingModelRespVO thingModel, Integer deviceType) { | ||
| 88 | - String superTableName = getSuperTableName(deviceType, thingModel.getProductKey()); | ||
| 89 | - try { | ||
| 90 | - List<TdFieldDO> oldFields = getTableFields(superTableName); | ||
| 91 | - List<TdFieldDO> newFields = FieldParser.parse(thingModel); | ||
| 92 | - | ||
| 93 | - updateTableFields(superTableName, oldFields, newFields); | ||
| 94 | - } catch (Exception e) { | ||
| 95 | - log.error("更新物模型超级表失败: {}", e.getMessage(), e); | ||
| 96 | - } | ||
| 97 | - } | ||
| 98 | - | ||
| 99 | - /** | ||
| 100 | - * 获取表的字段信息 | ||
| 101 | - */ | ||
| 102 | - private List<TdFieldDO> getTableFields(String tableName) { | ||
| 103 | - List<Map<String, Object>> tableDescription = tdEngineDDLMapper.describeSuperTable(new TdTableDO(getDatabaseName(), tableName)); | ||
| 104 | - if (CollUtil.isEmpty(tableDescription)) { | ||
| 105 | - return Collections.emptyList(); | ||
| 106 | - } | ||
| 107 | - | ||
| 108 | - return tableDescription.stream() | ||
| 109 | - .filter(map -> !"TAG".equals(map.get("note"))) | ||
| 110 | - .filter(map -> !"time".equals(map.get("field"))) | ||
| 111 | - .map(map -> TdFieldDO.builder() | ||
| 112 | - .fieldName((String) map.get("field")) | ||
| 113 | - .dataType((String) map.get("type")) | ||
| 114 | - .dataLength((Integer) map.get("length")) | ||
| 115 | - .build()) | ||
| 116 | - .collect(Collectors.toList()); | ||
| 117 | - } | ||
| 118 | - | ||
| 119 | - /** | ||
| 120 | - * 更新表的字段,包括新增、修改和删除字段 | ||
| 121 | - */ | ||
| 122 | - private void updateTableFields(String tableName, List<TdFieldDO> oldFields, List<TdFieldDO> newFields) { | ||
| 123 | - String databaseName = getDatabaseName(); | ||
| 124 | - | ||
| 125 | - // 获取新增、修改、删除的字段 | ||
| 126 | - List<TdFieldDO> addFields = getAddFields(oldFields, newFields); | ||
| 127 | - List<TdFieldDO> modifyFields = getModifyFields(oldFields, newFields); | ||
| 128 | - List<TdFieldDO> dropFields = getDropFields(oldFields, newFields); | ||
| 129 | - | ||
| 130 | - // 添加新增字段 | ||
| 131 | - if (CollUtil.isNotEmpty(addFields)) { | ||
| 132 | - for (TdFieldDO addField : addFields) { | ||
| 133 | - tdEngineDDLMapper.addColumnForSuperTable(TdTableDO.builder() | ||
| 134 | - .dataBaseName(databaseName) | ||
| 135 | - .superTableName(tableName) | ||
| 136 | - .column(addField) | ||
| 137 | - .build()); | ||
| 138 | - } | ||
| 139 | - } | ||
| 140 | - // 删除旧字段 | ||
| 141 | - if (CollUtil.isNotEmpty(dropFields)) { | ||
| 142 | - for (TdFieldDO dropField : dropFields) { | ||
| 143 | - tdEngineDDLMapper.dropColumnForSuperTable(TdTableDO.builder() | ||
| 144 | - .dataBaseName(databaseName) | ||
| 145 | - .superTableName(tableName) | ||
| 146 | - .column(dropField) | ||
| 147 | - .build()); | ||
| 148 | - } | ||
| 149 | - } | ||
| 150 | - // 修改字段(先删除再添加) | ||
| 151 | - if (CollUtil.isNotEmpty(modifyFields)) { | ||
| 152 | - for (TdFieldDO modifyField : modifyFields) { | ||
| 153 | - tdEngineDDLMapper.dropColumnForSuperTable(TdTableDO.builder() | ||
| 154 | - .dataBaseName(databaseName) | ||
| 155 | - .superTableName(tableName) | ||
| 156 | - .column(modifyField) | ||
| 157 | - .build()); | ||
| 158 | - tdEngineDDLMapper.addColumnForSuperTable(TdTableDO.builder() | ||
| 159 | - .dataBaseName(databaseName) | ||
| 160 | - .superTableName(tableName) | ||
| 161 | - .column(modifyField) | ||
| 162 | - .build()); | ||
| 163 | - } | ||
| 164 | - } | ||
| 165 | - } | ||
| 166 | - | ||
| 167 | - /** | ||
| 168 | - * 获取需要新增的字段 | ||
| 169 | - */ | ||
| 170 | - private List<TdFieldDO> getAddFields(List<TdFieldDO> oldFields, List<TdFieldDO> newFields) { | ||
| 171 | - Set<String> oldFieldNames = oldFields.stream() | ||
| 172 | - .map(TdFieldDO::getFieldName) | ||
| 173 | - .collect(Collectors.toSet()); | ||
| 174 | - return newFields.stream() | ||
| 175 | - .filter(f -> !oldFieldNames.contains(f.getFieldName())) | ||
| 176 | - .collect(Collectors.toList()); | ||
| 177 | - } | ||
| 178 | - | ||
| 179 | - /** | ||
| 180 | - * 获取需要修改的字段 | ||
| 181 | - */ | ||
| 182 | - private List<TdFieldDO> getModifyFields(List<TdFieldDO> oldFields, List<TdFieldDO> newFields) { | ||
| 183 | - Map<String, TdFieldDO> oldFieldMap = oldFields.stream() | ||
| 184 | - .collect(Collectors.toMap(TdFieldDO::getFieldName, f -> f)); | ||
| 185 | - | ||
| 186 | - return newFields.stream() | ||
| 187 | - .filter(f -> { | ||
| 188 | - TdFieldDO oldField = oldFieldMap.get(f.getFieldName()); | ||
| 189 | - return oldField != null && ( | ||
| 190 | - !oldField.getDataType().equals(f.getDataType()) || | ||
| 191 | - !Objects.equals(oldField.getDataLength(), f.getDataLength()) | ||
| 192 | - ); | ||
| 193 | - }) | ||
| 194 | - .collect(Collectors.toList()); | ||
| 195 | - } | ||
| 196 | - | ||
| 197 | - /** | ||
| 198 | - * 获取需要删除的字段 | ||
| 199 | - */ | ||
| 200 | - private List<TdFieldDO> getDropFields(List<TdFieldDO> oldFields, List<TdFieldDO> newFields) { | ||
| 201 | - Set<String> newFieldNames = newFields.stream() | ||
| 202 | - .map(TdFieldDO::getFieldName) | ||
| 203 | - .collect(Collectors.toSet()); | ||
| 204 | - return oldFields.stream() | ||
| 205 | - .filter(f -> !"time".equals(f.getFieldName())) | ||
| 206 | - .filter(f -> !newFieldNames.contains(f.getFieldName())) | ||
| 207 | - .collect(Collectors.toList()); | ||
| 208 | - } | ||
| 209 | - | ||
| 210 | - /** | ||
| 211 | - * 构建物模型 | ||
| 212 | - */ | ||
| 213 | - private ThingModelRespVO buildThingModel(IotProductDO product, List<IotProductThingModelDO> thingModelList) { | ||
| 214 | - ThingModelRespVO thingModel = new ThingModelRespVO(); | ||
| 215 | - thingModel.setId(product.getId()); | ||
| 216 | - thingModel.setProductKey(product.getProductKey()); | ||
| 217 | - | ||
| 218 | - List<ThingModelProperty> properties = thingModelList.stream() | ||
| 219 | - .filter(item -> IotProductThingModelTypeEnum.PROPERTY.equals( | ||
| 220 | - IotProductThingModelTypeEnum.valueOfType(item.getType()))) | ||
| 221 | - .map(this::buildThingModelProperty) | ||
| 222 | - .collect(Collectors.toList()); | ||
| 223 | - | ||
| 224 | - ThingModelRespVO.Model model = new ThingModelRespVO.Model(); | ||
| 225 | - model.setProperties(properties); | ||
| 226 | - thingModel.setModel(model); | ||
| 227 | - | ||
| 228 | - return thingModel; | ||
| 229 | - } | ||
| 230 | - | ||
| 231 | - /** | ||
| 232 | - * 构建物模型属性 | ||
| 233 | - */ | ||
| 234 | - private ThingModelProperty buildThingModelProperty(IotProductThingModelDO thingModel) { | ||
| 235 | - ThingModelProperty property = BeanUtil.copyProperties(thingModel, ThingModelProperty.class); | ||
| 236 | - property.setDataType(thingModel.getProperty().getDataType()); | ||
| 237 | - return property; | ||
| 238 | - } | ||
| 239 | - | ||
| 240 | - /** | ||
| 241 | - * 获取数据库名称 | ||
| 242 | - */ | ||
| 243 | - private String getDatabaseName() { | ||
| 244 | - int index = url.lastIndexOf("/"); | ||
| 245 | - return index != -1 ? url.substring(index + 1) : url; | ||
| 246 | - } | ||
| 247 | - | ||
| 248 | - /** | ||
| 249 | - * 获取超级表名称 | ||
| 250 | - */ | ||
| 251 | - private String getSuperTableName(Integer deviceType, String productKey) { | ||
| 252 | - String prefix = switch (deviceType) { | ||
| 253 | - case 1 -> "gateway_sub_"; | ||
| 254 | - case 2 -> "gateway_"; | ||
| 255 | - default -> "device_"; | ||
| 256 | - }; | ||
| 257 | - return (prefix + productKey).toLowerCase(); | ||
| 258 | - } | ||
| 259 | - | ||
| 260 | -} | ||
| 261 | \ No newline at end of file | 0 | \ No newline at end of file |
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; | @@ -11,16 +11,16 @@ import cn.iocoder.yudao.module.iot.dal.dataobject.tdengine.FieldParser; | ||
| 11 | import cn.iocoder.yudao.module.iot.dal.dataobject.tdengine.TdFieldDO; | 11 | import cn.iocoder.yudao.module.iot.dal.dataobject.tdengine.TdFieldDO; |
| 12 | import cn.iocoder.yudao.module.iot.dal.dataobject.tdengine.TdTableDO; | 12 | import cn.iocoder.yudao.module.iot.dal.dataobject.tdengine.TdTableDO; |
| 13 | import cn.iocoder.yudao.module.iot.dal.dataobject.tdengine.ThingModelMessage; | 13 | import cn.iocoder.yudao.module.iot.dal.dataobject.tdengine.ThingModelMessage; |
| 14 | -import cn.iocoder.yudao.module.iot.dal.dataobject.thingmodel.IotProductThingModelDO; | 14 | +import cn.iocoder.yudao.module.iot.dal.dataobject.thingmodel.IotThingModelDO; |
| 15 | import cn.iocoder.yudao.module.iot.dal.dataobject.product.IotProductDO; | 15 | import cn.iocoder.yudao.module.iot.dal.dataobject.product.IotProductDO; |
| 16 | import cn.iocoder.yudao.module.iot.dal.redis.deviceData.DeviceDataRedisDAO; | 16 | import cn.iocoder.yudao.module.iot.dal.redis.deviceData.DeviceDataRedisDAO; |
| 17 | import cn.iocoder.yudao.module.iot.dal.tdengine.TdEngineDDLMapper; | 17 | import cn.iocoder.yudao.module.iot.dal.tdengine.TdEngineDDLMapper; |
| 18 | import cn.iocoder.yudao.module.iot.dal.tdengine.TdEngineDMLMapper; | 18 | import cn.iocoder.yudao.module.iot.dal.tdengine.TdEngineDMLMapper; |
| 19 | import cn.iocoder.yudao.module.iot.enums.IotConstants; | 19 | import cn.iocoder.yudao.module.iot.enums.IotConstants; |
| 20 | import cn.iocoder.yudao.module.iot.enums.device.IotDeviceStatusEnum; | 20 | import cn.iocoder.yudao.module.iot.enums.device.IotDeviceStatusEnum; |
| 21 | -import cn.iocoder.yudao.module.iot.enums.thingmodel.IotProductThingModelTypeEnum; | 21 | +import cn.iocoder.yudao.module.iot.enums.thingmodel.IotThingModelTypeEnum; |
| 22 | import cn.iocoder.yudao.module.iot.service.device.IotDeviceService; | 22 | import cn.iocoder.yudao.module.iot.service.device.IotDeviceService; |
| 23 | -import cn.iocoder.yudao.module.iot.service.thingmodel.IotProductThingModelService; | 23 | +import cn.iocoder.yudao.module.iot.service.thingmodel.IotThingModelService; |
| 24 | import cn.iocoder.yudao.module.iot.service.product.IotProductService; | 24 | import cn.iocoder.yudao.module.iot.service.product.IotProductService; |
| 25 | import cn.iocoder.yudao.module.iot.util.IotTdDatabaseUtils; | 25 | import cn.iocoder.yudao.module.iot.util.IotTdDatabaseUtils; |
| 26 | import jakarta.annotation.Resource; | 26 | import jakarta.annotation.Resource; |
| @@ -52,7 +52,7 @@ public class IotThingModelMessageServiceImpl implements IotThingModelMessageServ | @@ -52,7 +52,7 @@ public class IotThingModelMessageServiceImpl implements IotThingModelMessageServ | ||
| 52 | private String url; | 52 | private String url; |
| 53 | 53 | ||
| 54 | @Resource | 54 | @Resource |
| 55 | - private IotProductThingModelService iotProductThingModelService; | 55 | + private IotThingModelService iotThingModelService; |
| 56 | @Resource | 56 | @Resource |
| 57 | private IotDeviceService iotDeviceService; | 57 | private IotDeviceService iotDeviceService; |
| 58 | @Resource | 58 | @Resource |
| @@ -83,7 +83,7 @@ public class IotThingModelMessageServiceImpl implements IotThingModelMessageServ | @@ -83,7 +83,7 @@ public class IotThingModelMessageServiceImpl implements IotThingModelMessageServ | ||
| 83 | 83 | ||
| 84 | // 2. 获取设备属性并进行物模型校验,过滤非物模型属性 | 84 | // 2. 获取设备属性并进行物模型校验,过滤非物模型属性 |
| 85 | Map<String, Object> params = thingModelMessage.dataToMap(); | 85 | Map<String, Object> params = thingModelMessage.dataToMap(); |
| 86 | - List<IotProductThingModelDO> thingModelList = getValidThingModelList(thingModelMessage.getProductKey()); | 86 | + List<IotThingModelDO> thingModelList = getValidThingModelList(thingModelMessage.getProductKey()); |
| 87 | if (thingModelList.isEmpty()) { | 87 | if (thingModelList.isEmpty()) { |
| 88 | return; | 88 | return; |
| 89 | } | 89 | } |
| @@ -102,9 +102,9 @@ public class IotThingModelMessageServiceImpl implements IotThingModelMessageServ | @@ -102,9 +102,9 @@ public class IotThingModelMessageServiceImpl implements IotThingModelMessageServ | ||
| 102 | .build()); | 102 | .build()); |
| 103 | } | 103 | } |
| 104 | 104 | ||
| 105 | - private List<IotProductThingModelDO> getValidThingModelList(String productKey) { | ||
| 106 | - return filterList(iotProductThingModelService.getProductThingModelListByProductKey(productKey), | ||
| 107 | - thingModel -> IotProductThingModelTypeEnum.PROPERTY.getType().equals(thingModel.getType())); | 105 | + private List<IotThingModelDO> getValidThingModelList(String productKey) { |
| 106 | + return filterList(iotThingModelService.getProductThingModelListByProductKey(productKey), | ||
| 107 | + thingModel -> IotThingModelTypeEnum.PROPERTY.getType().equals(thingModel.getType())); | ||
| 108 | } | 108 | } |
| 109 | 109 | ||
| 110 | @Override | 110 | @Override |
| @@ -134,21 +134,17 @@ public class IotThingModelMessageServiceImpl implements IotThingModelMessageServ | @@ -134,21 +134,17 @@ public class IotThingModelMessageServiceImpl implements IotThingModelMessageServ | ||
| 134 | tdEngineDDLMapper.createSuperTable(new TdTableDO(databaseName, superTableName, schemaFields, tagsFields)); | 134 | tdEngineDDLMapper.createSuperTable(new TdTableDO(databaseName, superTableName, schemaFields, tagsFields)); |
| 135 | } | 135 | } |
| 136 | 136 | ||
| 137 | - private List<IotProductThingModelDO> getValidFunctionList(String productKey) { | ||
| 138 | - // TODO @puhui999:使用 convertList 会好点哈 | ||
| 139 | - return iotProductThingModelService | ||
| 140 | - .getProductThingModelListByProductKey(productKey) | ||
| 141 | - .stream() | ||
| 142 | - .filter(function -> IotProductThingModelTypeEnum.PROPERTY.getType().equals(function.getType())) | ||
| 143 | - .toList(); | 137 | + private List<IotThingModelDO> getValidFunctionList(String productKey) { |
| 138 | + return filterList(iotThingModelService.getProductThingModelListByProductKey(productKey), | ||
| 139 | + thingModel -> IotThingModelTypeEnum.PROPERTY.getType().equals(thingModel.getType())); | ||
| 144 | } | 140 | } |
| 145 | 141 | ||
| 146 | - private List<TdFieldDO> filterAndCollectValidFields(Map<String, Object> params, List<IotProductThingModelDO> thingModelList, IotDeviceDO device, Long time) { | 142 | + private List<TdFieldDO> filterAndCollectValidFields(Map<String, Object> params, List<IotThingModelDO> thingModelList, IotDeviceDO device, Long time) { |
| 147 | // 1. 获取属性标识符集合 | 143 | // 1. 获取属性标识符集合 |
| 148 | - Set<String> propertyIdentifiers = convertSet(thingModelList, IotProductThingModelDO::getIdentifier); | 144 | + Set<String> propertyIdentifiers = convertSet(thingModelList, IotThingModelDO::getIdentifier); |
| 149 | 145 | ||
| 150 | // 2. 构建属性标识符和属性的映射 | 146 | // 2. 构建属性标识符和属性的映射 |
| 151 | - Map<String, IotProductThingModelDO> thingModelMap = convertMap(thingModelList, IotProductThingModelDO::getIdentifier); | 147 | + Map<String, IotThingModelDO> thingModelMap = convertMap(thingModelList, IotThingModelDO::getIdentifier); |
| 152 | 148 | ||
| 153 | // 3. 过滤并收集有效的属性字段 | 149 | // 3. 过滤并收集有效的属性字段 |
| 154 | List<TdFieldDO> schemaFieldValues = new ArrayList<>(); | 150 | List<TdFieldDO> schemaFieldValues = new ArrayList<>(); |
| @@ -168,21 +164,21 @@ public class IotThingModelMessageServiceImpl implements IotThingModelMessageServ | @@ -168,21 +164,21 @@ public class IotThingModelMessageServiceImpl implements IotThingModelMessageServ | ||
| 168 | * 缓存设备属性 | 164 | * 缓存设备属性 |
| 169 | * | 165 | * |
| 170 | * @param device 设备信息 | 166 | * @param device 设备信息 |
| 171 | - * @param iotProductThingModelDO 物模型属性 | 167 | + * @param iotThingModelDO 物模型属性 |
| 172 | * @param val 属性值 | 168 | * @param val 属性值 |
| 173 | * @param time 时间 | 169 | * @param time 时间 |
| 174 | */ | 170 | */ |
| 175 | - private void setDeviceDataCache(IotDeviceDO device, IotProductThingModelDO iotProductThingModelDO, Object val, Long time) { | 171 | + private void setDeviceDataCache(IotDeviceDO device, IotThingModelDO iotThingModelDO, Object val, Long time) { |
| 176 | IotDeviceDataDO deviceData = IotDeviceDataDO.builder() | 172 | IotDeviceDataDO deviceData = IotDeviceDataDO.builder() |
| 177 | .productKey(device.getProductKey()) | 173 | .productKey(device.getProductKey()) |
| 178 | .deviceName(device.getDeviceName()) | 174 | .deviceName(device.getDeviceName()) |
| 179 | - .identifier(iotProductThingModelDO.getIdentifier()) | 175 | + .identifier(iotThingModelDO.getIdentifier()) |
| 180 | .value(val != null ? val.toString() : null) | 176 | .value(val != null ? val.toString() : null) |
| 181 | .updateTime(DateUtil.toLocalDateTime(new Date(time))) | 177 | .updateTime(DateUtil.toLocalDateTime(new Date(time))) |
| 182 | .deviceId(device.getId()) | 178 | .deviceId(device.getId()) |
| 183 | - .thingModelId(iotProductThingModelDO.getId()) | ||
| 184 | - .name(iotProductThingModelDO.getName()) | ||
| 185 | - .dataType(iotProductThingModelDO.getProperty().getDataType()) | 179 | + .thingModelId(iotThingModelDO.getId()) |
| 180 | + .name(iotThingModelDO.getName()) | ||
| 181 | + .dataType(iotThingModelDO.getProperty().getDataType()) | ||
| 186 | .build(); | 182 | .build(); |
| 187 | deviceDataRedisDAO.set(deviceData); | 183 | deviceDataRedisDAO.set(deviceData); |
| 188 | } | 184 | } |
yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/thingmodel/IotProductThingModelServiceImpl.java deleted
| 1 | -package cn.iocoder.yudao.module.iot.service.thingmodel; | ||
| 2 | - | ||
| 3 | -import cn.hutool.core.collection.CollUtil; | ||
| 4 | -import cn.hutool.core.util.ObjectUtil; | ||
| 5 | -import cn.iocoder.yudao.framework.common.pojo.PageResult; | ||
| 6 | -import cn.iocoder.yudao.framework.common.util.collection.CollectionUtils; | ||
| 7 | -import cn.iocoder.yudao.framework.common.util.object.ObjectUtils; | ||
| 8 | -import cn.iocoder.yudao.module.iot.controller.admin.thingmodel.model.ThingModelEvent; | ||
| 9 | -import cn.iocoder.yudao.module.iot.controller.admin.thingmodel.model.ThingModelProperty; | ||
| 10 | -import cn.iocoder.yudao.module.iot.controller.admin.thingmodel.model.ThingModelService; | ||
| 11 | -import cn.iocoder.yudao.module.iot.controller.admin.thingmodel.model.dataType.ThingModelArgument; | ||
| 12 | -import cn.iocoder.yudao.module.iot.controller.admin.thingmodel.model.dataType.ThingModelArrayDataSpecs; | ||
| 13 | -import cn.iocoder.yudao.module.iot.controller.admin.thingmodel.model.dataType.ThingModelDateOrTextDataSpecs; | ||
| 14 | -import cn.iocoder.yudao.module.iot.controller.admin.thingmodel.vo.IotProductThingModelPageReqVO; | ||
| 15 | -import cn.iocoder.yudao.module.iot.controller.admin.thingmodel.vo.IotProductThingModelSaveReqVO; | ||
| 16 | -import cn.iocoder.yudao.module.iot.convert.thingmodel.IotProductThingModelConvert; | ||
| 17 | -import cn.iocoder.yudao.module.iot.dal.dataobject.product.IotProductDO; | ||
| 18 | -import cn.iocoder.yudao.module.iot.dal.dataobject.thingmodel.IotProductThingModelDO; | ||
| 19 | -import cn.iocoder.yudao.module.iot.dal.mysql.thingmodel.IotProductThingModelMapper; | ||
| 20 | -import cn.iocoder.yudao.module.iot.enums.product.IotProductStatusEnum; | ||
| 21 | -import cn.iocoder.yudao.module.iot.enums.thingmodel.IotProductThingModelAccessModeEnum; | ||
| 22 | -import cn.iocoder.yudao.module.iot.enums.thingmodel.IotProductThingModelTypeEnum; | ||
| 23 | -import cn.iocoder.yudao.module.iot.service.product.IotProductService; | ||
| 24 | -import cn.iocoder.yudao.module.iot.service.tdengine.IotSuperTableService; | ||
| 25 | -import jakarta.annotation.Resource; | ||
| 26 | -import lombok.extern.slf4j.Slf4j; | ||
| 27 | -import org.springframework.stereotype.Service; | ||
| 28 | -import org.springframework.transaction.annotation.Transactional; | ||
| 29 | -import org.springframework.validation.annotation.Validated; | ||
| 30 | - | ||
| 31 | -import java.util.*; | ||
| 32 | -import java.util.stream.Collectors; | ||
| 33 | - | ||
| 34 | -import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception; | ||
| 35 | -import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.diffList; | ||
| 36 | -import static cn.iocoder.yudao.module.iot.enums.ErrorCodeConstants.*; | ||
| 37 | - | ||
| 38 | -/** | ||
| 39 | - * IoT 产品物模型 Service 实现类 | ||
| 40 | - * | ||
| 41 | - * @author 芋道源码 | ||
| 42 | - */ | ||
| 43 | -@Service | ||
| 44 | -@Validated | ||
| 45 | -@Slf4j | ||
| 46 | -public class IotProductThingModelServiceImpl implements IotProductThingModelService { | ||
| 47 | - | ||
| 48 | - @Resource | ||
| 49 | - private IotProductThingModelMapper productThingModelMapper; | ||
| 50 | - | ||
| 51 | - @Resource | ||
| 52 | - private IotProductService productService; | ||
| 53 | - @Resource | ||
| 54 | - private IotSuperTableService dbStructureDataService; | ||
| 55 | - | ||
| 56 | - @Override | ||
| 57 | - @Transactional(rollbackFor = Exception.class) | ||
| 58 | - public Long createProductThingModel(IotProductThingModelSaveReqVO createReqVO) { | ||
| 59 | - // 1. 校验功能标识符在同一产品下是否唯一 | ||
| 60 | - validateIdentifierUnique(createReqVO.getProductId(), createReqVO.getIdentifier()); | ||
| 61 | - | ||
| 62 | - // 2. 功能名称在同一产品下是否唯一 | ||
| 63 | - validateNameUnique(createReqVO.getProductId(), createReqVO.getName()); | ||
| 64 | - | ||
| 65 | - // 3. 系统保留字段,不能用于标识符定义 | ||
| 66 | - validateNotDefaultEventAndService(createReqVO.getIdentifier()); | ||
| 67 | - | ||
| 68 | - // 4. 校验产品状态,发布状态下,不允许新增功能 | ||
| 69 | - validateProductStatus(createReqVO.getProductId()); | ||
| 70 | - | ||
| 71 | - // 5. 插入数据库 | ||
| 72 | - IotProductThingModelDO thingModel = IotProductThingModelConvert.INSTANCE.convert(createReqVO); | ||
| 73 | - productThingModelMapper.insert(thingModel); | ||
| 74 | - | ||
| 75 | - // 6. 如果创建的是属性,需要更新默认的事件和服务 | ||
| 76 | - if (Objects.equals(createReqVO.getType(), IotProductThingModelTypeEnum.PROPERTY.getType())) { | ||
| 77 | - createDefaultEventsAndServices(createReqVO.getProductId(), createReqVO.getProductKey()); | ||
| 78 | - } | ||
| 79 | - return thingModel.getId(); | ||
| 80 | - } | ||
| 81 | - | ||
| 82 | - private void validateProductStatus(Long createReqVO) { | ||
| 83 | - IotProductDO product = productService.getProduct(createReqVO); | ||
| 84 | - if (Objects.equals(product.getStatus(), IotProductStatusEnum.PUBLISHED.getStatus())) { | ||
| 85 | - throw exception(PRODUCT_STATUS_NOT_ALLOW_THING_MODEL); | ||
| 86 | - } | ||
| 87 | - } | ||
| 88 | - | ||
| 89 | - private void validateNotDefaultEventAndService(String identifier) { | ||
| 90 | - // set, get, post, property, event, time, value 是系统保留字段,不能用于标识符定义 | ||
| 91 | - if (CollUtil.containsAny(Arrays.asList("set", "get", "post", "property", "event", "time", "value"), Collections.singletonList(identifier))) { | ||
| 92 | - throw exception(THING_MODEL_IDENTIFIER_INVALID); | ||
| 93 | - } | ||
| 94 | - } | ||
| 95 | - | ||
| 96 | - private void validateNameUnique(Long productId, String name) { | ||
| 97 | - IotProductThingModelDO thingModel = productThingModelMapper.selectByProductIdAndName(productId, name); | ||
| 98 | - if (thingModel != null) { | ||
| 99 | - throw exception(THING_MODEL_NAME_EXISTS); | ||
| 100 | - } | ||
| 101 | - } | ||
| 102 | - | ||
| 103 | - private void validateIdentifierUnique(Long productId, String identifier) { | ||
| 104 | - IotProductThingModelDO thingModel = productThingModelMapper.selectByProductIdAndIdentifier(productId, identifier); | ||
| 105 | - if (thingModel != null) { | ||
| 106 | - throw exception(THING_MODEL_IDENTIFIER_EXISTS); | ||
| 107 | - } | ||
| 108 | - } | ||
| 109 | - | ||
| 110 | - @Override | ||
| 111 | - @Transactional(rollbackFor = Exception.class) | ||
| 112 | - public void updateProductThingModel(IotProductThingModelSaveReqVO updateReqVO) { | ||
| 113 | - // 1. 校验功能是否存在 | ||
| 114 | - validateProductThingModelMapperExists(updateReqVO.getId()); | ||
| 115 | - | ||
| 116 | - // 2. 校验功能标识符是否唯一 | ||
| 117 | - validateIdentifierUniqueForUpdate(updateReqVO.getId(), updateReqVO.getProductId(), updateReqVO.getIdentifier()); | ||
| 118 | - | ||
| 119 | - // 3. 校验产品状态,发布状态下,不允许操作功能 | ||
| 120 | - validateProductStatus(updateReqVO.getProductId()); | ||
| 121 | - | ||
| 122 | - // 4. 更新数据库 | ||
| 123 | - IotProductThingModelDO thingModel = IotProductThingModelConvert.INSTANCE.convert(updateReqVO); | ||
| 124 | - productThingModelMapper.updateById(thingModel); | ||
| 125 | - | ||
| 126 | - // 5. 如果更新的是属性,需要更新默认的事件和服务 | ||
| 127 | - if (Objects.equals(updateReqVO.getType(), IotProductThingModelTypeEnum.PROPERTY.getType())) { | ||
| 128 | - createDefaultEventsAndServices(updateReqVO.getProductId(), updateReqVO.getProductKey()); | ||
| 129 | - } | ||
| 130 | - } | ||
| 131 | - | ||
| 132 | - private void validateIdentifierUniqueForUpdate(Long id, Long productId, String identifier) { | ||
| 133 | - IotProductThingModelDO thingModel = productThingModelMapper.selectByProductIdAndIdentifier(productId, identifier); | ||
| 134 | - if (thingModel != null && ObjectUtil.notEqual(thingModel.getId(), id)) { | ||
| 135 | - throw exception(THING_MODEL_IDENTIFIER_EXISTS); | ||
| 136 | - } | ||
| 137 | - } | ||
| 138 | - | ||
| 139 | - @Override | ||
| 140 | - @Transactional(rollbackFor = Exception.class) | ||
| 141 | - public void deleteProductThingModel(Long id) { | ||
| 142 | - // 1. 校验功能是否存在 | ||
| 143 | - IotProductThingModelDO thingModel = productThingModelMapper.selectById(id); | ||
| 144 | - if (thingModel == null) { | ||
| 145 | - throw exception(THING_MODEL_NOT_EXISTS); | ||
| 146 | - } | ||
| 147 | - | ||
| 148 | - // 3. 校验产品状态,发布状态下,不允许操作功能 | ||
| 149 | - validateProductStatus(thingModel.getProductId()); | ||
| 150 | - | ||
| 151 | - // 2. 删除功能 | ||
| 152 | - productThingModelMapper.deleteById(id); | ||
| 153 | - | ||
| 154 | - // 3. 如果删除的是属性,需要更新默认的事件和服务 | ||
| 155 | - if (Objects.equals(thingModel.getType(), IotProductThingModelTypeEnum.PROPERTY.getType())) { | ||
| 156 | - createDefaultEventsAndServices(thingModel.getProductId(), thingModel.getProductKey()); | ||
| 157 | - } | ||
| 158 | - } | ||
| 159 | - | ||
| 160 | - /** | ||
| 161 | - * 校验功能是否存在 | ||
| 162 | - * | ||
| 163 | - * @param id 功能编号 | ||
| 164 | - */ | ||
| 165 | - private void validateProductThingModelMapperExists(Long id) { | ||
| 166 | - if (productThingModelMapper.selectById(id) == null) { | ||
| 167 | - throw exception(THING_MODEL_NOT_EXISTS); | ||
| 168 | - } | ||
| 169 | - } | ||
| 170 | - | ||
| 171 | - @Override | ||
| 172 | - public IotProductThingModelDO getProductThingModel(Long id) { | ||
| 173 | - return productThingModelMapper.selectById(id); | ||
| 174 | - } | ||
| 175 | - | ||
| 176 | - @Override | ||
| 177 | - public List<IotProductThingModelDO> getProductThingModelListByProductId(Long productId) { | ||
| 178 | - return productThingModelMapper.selectListByProductId(productId); | ||
| 179 | - } | ||
| 180 | - | ||
| 181 | - @Override | ||
| 182 | - public PageResult<IotProductThingModelDO> getProductThingModelPage(IotProductThingModelPageReqVO pageReqVO) { | ||
| 183 | - return productThingModelMapper.selectPage(pageReqVO); | ||
| 184 | - } | ||
| 185 | - | ||
| 186 | - @Override | ||
| 187 | - public void createSuperTableDataModel(Long productId) { | ||
| 188 | - // 1. 查询产品 | ||
| 189 | - IotProductDO product = productService.getProduct(productId); | ||
| 190 | - | ||
| 191 | - // 2. 查询产品的物模型功能列表 | ||
| 192 | - List<IotProductThingModelDO> thingModelList = productThingModelMapper.selectListByProductId(productId); | ||
| 193 | - | ||
| 194 | - // 3. 生成 TDengine 的数据模型 | ||
| 195 | - dbStructureDataService.createSuperTableDataModel(product, thingModelList); | ||
| 196 | - } | ||
| 197 | - | ||
| 198 | - @Override | ||
| 199 | - public List<IotProductThingModelDO> getProductThingModelListByProductKey(String productKey) { | ||
| 200 | - return productThingModelMapper.selectListByProductKey(productKey); | ||
| 201 | - } | ||
| 202 | - | ||
| 203 | - /** | ||
| 204 | - * 创建默认的事件和服务 | ||
| 205 | - */ | ||
| 206 | - public void createDefaultEventsAndServices(Long productId, String productKey) { | ||
| 207 | - // 1. 获取当前属性列表 | ||
| 208 | - List<IotProductThingModelDO> propertyList = productThingModelMapper | ||
| 209 | - .selectListByProductIdAndType(productId, IotProductThingModelTypeEnum.PROPERTY.getType()); | ||
| 210 | - | ||
| 211 | - // 2. 生成新的事件和服务列表 | ||
| 212 | - List<IotProductThingModelDO> newThingModelList = new ArrayList<>(); | ||
| 213 | - // 生成属性上报事件 | ||
| 214 | - ThingModelEvent propertyPostEvent = generatePropertyPostEvent(propertyList); | ||
| 215 | - if (propertyPostEvent != null) { | ||
| 216 | - IotProductThingModelDO eventThingModel = buildEventThingModelDO(productId, productKey, propertyPostEvent); | ||
| 217 | - newThingModelList.add(eventThingModel); | ||
| 218 | - } | ||
| 219 | - // 生成属性设置服务 | ||
| 220 | - ThingModelService propertySetService = generatePropertySetService(propertyList); | ||
| 221 | - if (propertySetService != null) { | ||
| 222 | - IotProductThingModelDO setServiceThingModel = buildServiceThingModelDO(productId, productKey, propertySetService); | ||
| 223 | - newThingModelList.add(setServiceThingModel); | ||
| 224 | - } | ||
| 225 | - // 生成属性获取服务 | ||
| 226 | - ThingModelService propertyGetService = generatePropertyGetService(propertyList); | ||
| 227 | - if (propertyGetService != null) { | ||
| 228 | - IotProductThingModelDO getServiceThingModel = buildServiceThingModelDO(productId, productKey, propertyGetService); | ||
| 229 | - newThingModelList.add(getServiceThingModel); | ||
| 230 | - } | ||
| 231 | - | ||
| 232 | - // 3. 获取数据库中的默认的旧事件和服务列表 | ||
| 233 | - List<IotProductThingModelDO> oldThingModelList = productThingModelMapper.selectListByProductIdAndIdentifiersAndTypes( | ||
| 234 | - productId, | ||
| 235 | - Arrays.asList("post", "set", "get"), | ||
| 236 | - Arrays.asList(IotProductThingModelTypeEnum.EVENT.getType(), IotProductThingModelTypeEnum.SERVICE.getType()) | ||
| 237 | - ); | ||
| 238 | - | ||
| 239 | - // 3.1 使用 diffList 方法比较新旧列表 | ||
| 240 | - List<List<IotProductThingModelDO>> diffResult = diffList(oldThingModelList, newThingModelList, | ||
| 241 | - // 继续使用 identifier 和 type 进行比较:这样可以准确地匹配对应的功能对象。 | ||
| 242 | - (oldFunc, newFunc) -> Objects.equals(oldFunc.getIdentifier(), newFunc.getIdentifier()) | ||
| 243 | - && Objects.equals(oldFunc.getType(), newFunc.getType())); | ||
| 244 | - List<IotProductThingModelDO> createList = diffResult.get(0); // 需要新增的 | ||
| 245 | - List<IotProductThingModelDO> updateList = diffResult.get(1); // 需要更新的 | ||
| 246 | - List<IotProductThingModelDO> deleteList = diffResult.get(2); // 需要删除的 | ||
| 247 | - | ||
| 248 | - // 3.2 批量执行数据库操作 | ||
| 249 | - // 新增数据库中的新事件和服务列表 | ||
| 250 | - if (CollUtil.isNotEmpty(createList)) { | ||
| 251 | - productThingModelMapper.insertBatch(createList); | ||
| 252 | - } | ||
| 253 | - // 更新数据库中的事件和服务列表 | ||
| 254 | - if (CollUtil.isNotEmpty(updateList)) { | ||
| 255 | - // 首先,为每个需要更新的对象设置其对应的 ID | ||
| 256 | - updateList.forEach(updateFunc -> { | ||
| 257 | - IotProductThingModelDO oldFunc = findThingModelByIdentifierAndType( | ||
| 258 | - oldThingModelList, updateFunc.getIdentifier(), updateFunc.getType()); | ||
| 259 | - if (oldFunc != null) { | ||
| 260 | - updateFunc.setId(oldFunc.getId()); | ||
| 261 | - } | ||
| 262 | - }); | ||
| 263 | - // 过滤掉没有设置 ID 的对象 | ||
| 264 | - List<IotProductThingModelDO> validUpdateList = updateList.stream() | ||
| 265 | - .filter(func -> func.getId() != null) | ||
| 266 | - .collect(Collectors.toList()); | ||
| 267 | - // 执行批量更新 | ||
| 268 | - if (CollUtil.isNotEmpty(validUpdateList)) { | ||
| 269 | - productThingModelMapper.updateBatch(validUpdateList); | ||
| 270 | - } | ||
| 271 | - } | ||
| 272 | - | ||
| 273 | - // 删除数据库中的旧事件和服务列表 | ||
| 274 | - if (CollUtil.isNotEmpty(deleteList)) { | ||
| 275 | - Set<Long> idsToDelete = CollectionUtils.convertSet(deleteList, IotProductThingModelDO::getId); | ||
| 276 | - productThingModelMapper.deleteByIds(idsToDelete); | ||
| 277 | - } | ||
| 278 | - } | ||
| 279 | - | ||
| 280 | - /** | ||
| 281 | - * 根据标识符和类型查找功能对象 | ||
| 282 | - */ | ||
| 283 | - private IotProductThingModelDO findThingModelByIdentifierAndType(List<IotProductThingModelDO> thingModelList, | ||
| 284 | - String identifier, Integer type) { | ||
| 285 | - return CollUtil.findOne(thingModelList, func -> | ||
| 286 | - Objects.equals(func.getIdentifier(), identifier) && Objects.equals(func.getType(), type)); | ||
| 287 | - } | ||
| 288 | - | ||
| 289 | - /** | ||
| 290 | - * 构建事件功能对象 | ||
| 291 | - */ | ||
| 292 | - private IotProductThingModelDO buildEventThingModelDO(Long productId, String productKey, ThingModelEvent event) { | ||
| 293 | - return new IotProductThingModelDO() | ||
| 294 | - .setProductId(productId) | ||
| 295 | - .setProductKey(productKey) | ||
| 296 | - .setIdentifier(event.getIdentifier()) | ||
| 297 | - .setName(event.getName()) | ||
| 298 | - .setDescription(event.getDescription()) | ||
| 299 | - .setType(IotProductThingModelTypeEnum.EVENT.getType()) | ||
| 300 | - .setEvent(event); | ||
| 301 | - } | ||
| 302 | - | ||
| 303 | - /** | ||
| 304 | - * 构建服务功能对象 | ||
| 305 | - */ | ||
| 306 | - private IotProductThingModelDO buildServiceThingModelDO(Long productId, String productKey, ThingModelService service) { | ||
| 307 | - return new IotProductThingModelDO() | ||
| 308 | - .setProductId(productId) | ||
| 309 | - .setProductKey(productKey) | ||
| 310 | - .setIdentifier(service.getIdentifier()) | ||
| 311 | - .setName(service.getName()) | ||
| 312 | - .setDescription(service.getDescription()) | ||
| 313 | - .setType(IotProductThingModelTypeEnum.SERVICE.getType()) | ||
| 314 | - .setService(service); | ||
| 315 | - } | ||
| 316 | - | ||
| 317 | - /** | ||
| 318 | - * 生成属性上报事件 | ||
| 319 | - */ | ||
| 320 | - private ThingModelEvent generatePropertyPostEvent(List<IotProductThingModelDO> propertyList) { | ||
| 321 | - if (CollUtil.isEmpty(propertyList)) { | ||
| 322 | - return null; | ||
| 323 | - } | ||
| 324 | - | ||
| 325 | - ThingModelEvent event = new ThingModelEvent() | ||
| 326 | - .setIdentifier("post") | ||
| 327 | - .setName("属性上报") | ||
| 328 | - .setType("info") | ||
| 329 | - .setDescription("属性上报事件") | ||
| 330 | - .setMethod("thing.event.property.post"); | ||
| 331 | - | ||
| 332 | - // 将属性列表转换为事件的输出参数 | ||
| 333 | - List<ThingModelArgument> outputData = new ArrayList<>(); | ||
| 334 | - for (IotProductThingModelDO thingModel : propertyList) { | ||
| 335 | - ThingModelArgument arg = new ThingModelArgument() | ||
| 336 | - .setIdentifier(thingModel.getIdentifier()) | ||
| 337 | - .setName(thingModel.getName()) | ||
| 338 | - .setProperty(thingModel.getProperty()) | ||
| 339 | - .setDescription(thingModel.getDescription()) | ||
| 340 | - .setDirection("output"); // 设置为输出参数 | ||
| 341 | - outputData.add(arg); | ||
| 342 | - } | ||
| 343 | - event.setOutputData(outputData); | ||
| 344 | - return event; | ||
| 345 | - } | ||
| 346 | - | ||
| 347 | - /** | ||
| 348 | - * 生成属性设置服务 | ||
| 349 | - */ | ||
| 350 | - private ThingModelService generatePropertySetService(List<IotProductThingModelDO> propertyList) { | ||
| 351 | - if (propertyList == null || propertyList.isEmpty()) { | ||
| 352 | - return null; | ||
| 353 | - } | ||
| 354 | - | ||
| 355 | - List<ThingModelArgument> inputData = new ArrayList<>(); | ||
| 356 | - for (IotProductThingModelDO thingModel : propertyList) { | ||
| 357 | - ThingModelProperty property = thingModel.getProperty(); | ||
| 358 | - if (IotProductThingModelAccessModeEnum.READ_WRITE.getMode().equals(property.getAccessMode())) { | ||
| 359 | - ThingModelArgument arg = new ThingModelArgument() | ||
| 360 | - .setIdentifier(property.getIdentifier()) | ||
| 361 | - .setName(property.getName()) | ||
| 362 | - .setProperty(property) | ||
| 363 | - .setDescription(property.getDescription()) | ||
| 364 | - .setDirection("input"); // 设置为输入参数 | ||
| 365 | - inputData.add(arg); | ||
| 366 | - } | ||
| 367 | - } | ||
| 368 | - if (inputData.isEmpty()) { | ||
| 369 | - // 如果没有可写属性,不生成属性设置服务 | ||
| 370 | - return null; | ||
| 371 | - } | ||
| 372 | - | ||
| 373 | - // 属性设置服务一般不需要输出参数 | ||
| 374 | - return new ThingModelService() | ||
| 375 | - .setIdentifier("set") | ||
| 376 | - .setName("属性设置") | ||
| 377 | - .setCallType("async") | ||
| 378 | - .setDescription("属性设置服务") | ||
| 379 | - .setMethod("thing.service.property.set") | ||
| 380 | - .setInputData(inputData) | ||
| 381 | - // 属性设置服务一般不需要输出参数 | ||
| 382 | - .setOutputData(new ArrayList<>()); | ||
| 383 | - } | ||
| 384 | - | ||
| 385 | - /** | ||
| 386 | - * 生成属性获取服务 | ||
| 387 | - */ | ||
| 388 | - private ThingModelService generatePropertyGetService(List<IotProductThingModelDO> propertyList) { | ||
| 389 | - if (propertyList == null || propertyList.isEmpty()) { | ||
| 390 | - return null; | ||
| 391 | - } | ||
| 392 | - | ||
| 393 | - List<ThingModelArgument> outputData = new ArrayList<>(); | ||
| 394 | - for (IotProductThingModelDO thingModelDO : propertyList) { | ||
| 395 | - ThingModelProperty property = thingModelDO.getProperty(); | ||
| 396 | - if (ObjectUtils.equalsAny(property.getAccessMode(), | ||
| 397 | - IotProductThingModelAccessModeEnum.READ_ONLY.getMode(), IotProductThingModelAccessModeEnum.READ_WRITE.getMode())) { | ||
| 398 | - ThingModelArgument arg = new ThingModelArgument() | ||
| 399 | - .setIdentifier(property.getIdentifier()) | ||
| 400 | - .setName(property.getName()) | ||
| 401 | - .setProperty(property) | ||
| 402 | - .setDescription(property.getDescription()) | ||
| 403 | - .setDirection("output"); // 设置为输出参数 | ||
| 404 | - outputData.add(arg); | ||
| 405 | - } | ||
| 406 | - } | ||
| 407 | - if (outputData.isEmpty()) { | ||
| 408 | - // 如果没有可读属性,不生成属性获取服务 | ||
| 409 | - return null; | ||
| 410 | - } | ||
| 411 | - | ||
| 412 | - ThingModelService service = new ThingModelService() | ||
| 413 | - .setIdentifier("get") | ||
| 414 | - .setName("属性获取") | ||
| 415 | - .setCallType("async") | ||
| 416 | - .setDescription("属性获取服务") | ||
| 417 | - .setMethod("thing.service.property.get"); | ||
| 418 | - | ||
| 419 | - // 定义输入参数:属性标识符列表 | ||
| 420 | - ThingModelArgument inputArg = new ThingModelArgument() | ||
| 421 | - .setIdentifier("properties") | ||
| 422 | - .setName("属性标识符列表") | ||
| 423 | - .setDescription("需要获取的属性标识符列表") | ||
| 424 | - .setDirection("input"); // 设置为输入参数 | ||
| 425 | - | ||
| 426 | - // 创建数组类型,元素类型为文本类型(字符串)TODO @puhui999: 还得研究研究 | ||
| 427 | - ThingModelArrayDataSpecs arrayType = new ThingModelArrayDataSpecs(); | ||
| 428 | - arrayType.setDataType("array"); | ||
| 429 | - inputArg.setProperty(new ThingModelProperty().setIdentifier(inputArg.getIdentifier()).setName(inputArg.getName()) | ||
| 430 | - .setDescription(inputArg.getDescription()).setDataSpecs(arrayType)); | ||
| 431 | - | ||
| 432 | - ThingModelDateOrTextDataSpecs textType = new ThingModelDateOrTextDataSpecs(); | ||
| 433 | - textType.setDataType("text"); | ||
| 434 | - inputArg.setProperty(new ThingModelProperty().setIdentifier(inputArg.getIdentifier()).setName(inputArg.getName()) | ||
| 435 | - .setDescription(inputArg.getDescription()).setDataSpecs(textType)); | ||
| 436 | - | ||
| 437 | - service.setInputData(Collections.singletonList(inputArg)); | ||
| 438 | - service.setOutputData(outputData); | ||
| 439 | - return service; | ||
| 440 | - } | ||
| 441 | - | ||
| 442 | -} |
yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/thingmodel/IotProductThingModelService.java renamed to yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/thingmodel/IotThingModelService.java
| 1 | package cn.iocoder.yudao.module.iot.service.thingmodel; | 1 | package cn.iocoder.yudao.module.iot.service.thingmodel; |
| 2 | 2 | ||
| 3 | import cn.iocoder.yudao.framework.common.pojo.PageResult; | 3 | import cn.iocoder.yudao.framework.common.pojo.PageResult; |
| 4 | -import cn.iocoder.yudao.module.iot.controller.admin.thingmodel.vo.IotProductThingModelPageReqVO; | ||
| 5 | -import cn.iocoder.yudao.module.iot.controller.admin.thingmodel.vo.IotProductThingModelSaveReqVO; | ||
| 6 | -import cn.iocoder.yudao.module.iot.dal.dataobject.thingmodel.IotProductThingModelDO; | 4 | +import cn.iocoder.yudao.module.iot.controller.admin.thingmodel.vo.IotThingModelPageReqVO; |
| 5 | +import cn.iocoder.yudao.module.iot.controller.admin.thingmodel.vo.IotThingModelSaveReqVO; | ||
| 6 | +import cn.iocoder.yudao.module.iot.dal.dataobject.thingmodel.IotThingModelDO; | ||
| 7 | import jakarta.validation.Valid; | 7 | import jakarta.validation.Valid; |
| 8 | 8 | ||
| 9 | import java.util.List; | 9 | import java.util.List; |
| @@ -13,7 +13,7 @@ import java.util.List; | @@ -13,7 +13,7 @@ import java.util.List; | ||
| 13 | * | 13 | * |
| 14 | * @author 芋道源码 | 14 | * @author 芋道源码 |
| 15 | */ | 15 | */ |
| 16 | -public interface IotProductThingModelService { | 16 | +public interface IotThingModelService { |
| 17 | 17 | ||
| 18 | /** | 18 | /** |
| 19 | * 创建产品物模型 | 19 | * 创建产品物模型 |
| @@ -21,21 +21,21 @@ public interface IotProductThingModelService { | @@ -21,21 +21,21 @@ public interface IotProductThingModelService { | ||
| 21 | * @param createReqVO 创建信息 | 21 | * @param createReqVO 创建信息 |
| 22 | * @return 编号 | 22 | * @return 编号 |
| 23 | */ | 23 | */ |
| 24 | - Long createProductThingModel(@Valid IotProductThingModelSaveReqVO createReqVO); | 24 | + Long createThingModel(@Valid IotThingModelSaveReqVO createReqVO); |
| 25 | 25 | ||
| 26 | /** | 26 | /** |
| 27 | * 更新产品物模型 | 27 | * 更新产品物模型 |
| 28 | * | 28 | * |
| 29 | * @param updateReqVO 更新信息 | 29 | * @param updateReqVO 更新信息 |
| 30 | */ | 30 | */ |
| 31 | - void updateProductThingModel(@Valid IotProductThingModelSaveReqVO updateReqVO); | 31 | + void updateThingModel(@Valid IotThingModelSaveReqVO updateReqVO); |
| 32 | 32 | ||
| 33 | /** | 33 | /** |
| 34 | * 删除产品物模型 | 34 | * 删除产品物模型 |
| 35 | * | 35 | * |
| 36 | * @param id 编号 | 36 | * @param id 编号 |
| 37 | */ | 37 | */ |
| 38 | - void deleteProductThingModel(Long id); | 38 | + void deleteThingModel(Long id); |
| 39 | 39 | ||
| 40 | /** | 40 | /** |
| 41 | * 获得产品物模型 | 41 | * 获得产品物模型 |
| @@ -43,7 +43,7 @@ public interface IotProductThingModelService { | @@ -43,7 +43,7 @@ public interface IotProductThingModelService { | ||
| 43 | * @param id 编号 | 43 | * @param id 编号 |
| 44 | * @return 产品物模型 | 44 | * @return 产品物模型 |
| 45 | */ | 45 | */ |
| 46 | - IotProductThingModelDO getProductThingModel(Long id); | 46 | + IotThingModelDO getThingModel(Long id); |
| 47 | 47 | ||
| 48 | /** | 48 | /** |
| 49 | * 获得产品物模型列表 | 49 | * 获得产品物模型列表 |
| @@ -51,7 +51,7 @@ public interface IotProductThingModelService { | @@ -51,7 +51,7 @@ public interface IotProductThingModelService { | ||
| 51 | * @param productId 产品编号 | 51 | * @param productId 产品编号 |
| 52 | * @return 产品物模型列表 | 52 | * @return 产品物模型列表 |
| 53 | */ | 53 | */ |
| 54 | - List<IotProductThingModelDO> getProductThingModelListByProductId(Long productId); | 54 | + List<IotThingModelDO> getThingModelListByProductId(Long productId); |
| 55 | 55 | ||
| 56 | /** | 56 | /** |
| 57 | * 获得产品物模型分页 | 57 | * 获得产品物模型分页 |
| @@ -59,14 +59,7 @@ public interface IotProductThingModelService { | @@ -59,14 +59,7 @@ public interface IotProductThingModelService { | ||
| 59 | * @param pageReqVO 分页查询 | 59 | * @param pageReqVO 分页查询 |
| 60 | * @return 产品物模型分页 | 60 | * @return 产品物模型分页 |
| 61 | */ | 61 | */ |
| 62 | - PageResult<IotProductThingModelDO> getProductThingModelPage(IotProductThingModelPageReqVO pageReqVO); | ||
| 63 | - | ||
| 64 | - /** | ||
| 65 | - * 创建超级表数据模型 | ||
| 66 | - * | ||
| 67 | - * @param productId 产品编号 | ||
| 68 | - */ | ||
| 69 | - void createSuperTableDataModel(Long productId); | 62 | + PageResult<IotThingModelDO> getProductThingModelPage(IotThingModelPageReqVO pageReqVO); |
| 70 | 63 | ||
| 71 | /** | 64 | /** |
| 72 | * 获得产品物模型列表 | 65 | * 获得产品物模型列表 |
| @@ -74,6 +67,6 @@ public interface IotProductThingModelService { | @@ -74,6 +67,6 @@ public interface IotProductThingModelService { | ||
| 74 | * @param productKey 产品 Key | 67 | * @param productKey 产品 Key |
| 75 | * @return 产品物模型列表 | 68 | * @return 产品物模型列表 |
| 76 | */ | 69 | */ |
| 77 | - List<IotProductThingModelDO> getProductThingModelListByProductKey(String productKey); | 70 | + List<IotThingModelDO> getProductThingModelListByProductKey(String productKey); |
| 78 | 71 | ||
| 79 | } | 72 | } |
| 80 | \ No newline at end of file | 73 | \ No newline at end of file |
yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/thingmodel/IotThingModelServiceImpl.java
0 → 100644
| 1 | +package cn.iocoder.yudao.module.iot.service.thingmodel; | ||
| 2 | + | ||
| 3 | +import cn.hutool.core.collection.CollUtil; | ||
| 4 | +import cn.hutool.core.util.ObjectUtil; | ||
| 5 | +import cn.iocoder.yudao.framework.common.pojo.PageResult; | ||
| 6 | +import cn.iocoder.yudao.framework.common.util.object.BeanUtils; | ||
| 7 | +import cn.iocoder.yudao.module.iot.controller.admin.thingmodel.model.ThingModelEvent; | ||
| 8 | +import cn.iocoder.yudao.module.iot.controller.admin.thingmodel.model.ThingModelParam; | ||
| 9 | +import cn.iocoder.yudao.module.iot.controller.admin.thingmodel.model.ThingModelService; | ||
| 10 | +import cn.iocoder.yudao.module.iot.controller.admin.thingmodel.vo.IotThingModelPageReqVO; | ||
| 11 | +import cn.iocoder.yudao.module.iot.controller.admin.thingmodel.vo.IotThingModelSaveReqVO; | ||
| 12 | +import cn.iocoder.yudao.module.iot.convert.thingmodel.IotThingModelConvert; | ||
| 13 | +import cn.iocoder.yudao.module.iot.dal.dataobject.product.IotProductDO; | ||
| 14 | +import cn.iocoder.yudao.module.iot.dal.dataobject.thingmodel.IotThingModelDO; | ||
| 15 | +import cn.iocoder.yudao.module.iot.dal.mysql.thingmodel.IotThingModelMapper; | ||
| 16 | +import cn.iocoder.yudao.module.iot.enums.product.IotProductStatusEnum; | ||
| 17 | +import cn.iocoder.yudao.module.iot.enums.thingmodel.*; | ||
| 18 | +import cn.iocoder.yudao.module.iot.service.product.IotProductService; | ||
| 19 | +import jakarta.annotation.Resource; | ||
| 20 | +import lombok.extern.slf4j.Slf4j; | ||
| 21 | +import org.springframework.stereotype.Service; | ||
| 22 | +import org.springframework.transaction.annotation.Transactional; | ||
| 23 | +import org.springframework.validation.annotation.Validated; | ||
| 24 | + | ||
| 25 | +import java.util.*; | ||
| 26 | + | ||
| 27 | +import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception; | ||
| 28 | +import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.*; | ||
| 29 | +import static cn.iocoder.yudao.module.iot.enums.ErrorCodeConstants.*; | ||
| 30 | + | ||
| 31 | +/** | ||
| 32 | + * IoT 产品物模型 Service 实现类 | ||
| 33 | + * | ||
| 34 | + * @author 芋道源码 | ||
| 35 | + */ | ||
| 36 | +@Service | ||
| 37 | +@Validated | ||
| 38 | +@Slf4j | ||
| 39 | +public class IotThingModelServiceImpl implements IotThingModelService { | ||
| 40 | + | ||
| 41 | + @Resource | ||
| 42 | + private IotThingModelMapper thingModelMapper; | ||
| 43 | + | ||
| 44 | + @Resource | ||
| 45 | + private IotProductService productService; | ||
| 46 | + | ||
| 47 | + @Override | ||
| 48 | + @Transactional(rollbackFor = Exception.class) | ||
| 49 | + public Long createThingModel(IotThingModelSaveReqVO createReqVO) { | ||
| 50 | + // 1. 校验功能标识符在同一产品下是否唯一 | ||
| 51 | + validateIdentifierUnique(createReqVO.getProductId(), createReqVO.getIdentifier()); | ||
| 52 | + | ||
| 53 | + // 2. 功能名称在同一产品下是否唯一 | ||
| 54 | + validateNameUnique(createReqVO.getProductId(), createReqVO.getName()); | ||
| 55 | + | ||
| 56 | + // 3. 系统保留字段,不能用于标识符定义 | ||
| 57 | + validateNotDefaultEventAndService(createReqVO.getIdentifier()); | ||
| 58 | + | ||
| 59 | + // 4. 校验产品状态,发布状态下,不允许新增功能 | ||
| 60 | + validateProductStatus(createReqVO.getProductId()); | ||
| 61 | + | ||
| 62 | + // 5. 插入数据库 | ||
| 63 | + IotThingModelDO thingModel = IotThingModelConvert.INSTANCE.convert(createReqVO); | ||
| 64 | + thingModelMapper.insert(thingModel); | ||
| 65 | + | ||
| 66 | + // 6. 如果创建的是属性,需要更新默认的事件和服务 | ||
| 67 | + if (Objects.equals(createReqVO.getType(), IotThingModelTypeEnum.PROPERTY.getType())) { | ||
| 68 | + createDefaultEventsAndServices(createReqVO.getProductId(), createReqVO.getProductKey()); | ||
| 69 | + } | ||
| 70 | + // TODO @puhui999: 服务和事件的情况 method 怎么设置?在前端设置还是后端设置? | ||
| 71 | + return thingModel.getId(); | ||
| 72 | + } | ||
| 73 | + | ||
| 74 | + @Override | ||
| 75 | + @Transactional(rollbackFor = Exception.class) | ||
| 76 | + public void updateThingModel(IotThingModelSaveReqVO updateReqVO) { | ||
| 77 | + // 1. 校验功能是否存在 | ||
| 78 | + validateProductThingModelMapperExists(updateReqVO.getId()); | ||
| 79 | + | ||
| 80 | + // 2. 校验功能标识符是否唯一 | ||
| 81 | + validateIdentifierUniqueForUpdate(updateReqVO.getId(), updateReqVO.getProductId(), updateReqVO.getIdentifier()); | ||
| 82 | + | ||
| 83 | + // 3. 校验产品状态,发布状态下,不允许操作功能 | ||
| 84 | + validateProductStatus(updateReqVO.getProductId()); | ||
| 85 | + | ||
| 86 | + // 4. 更新数据库 | ||
| 87 | + IotThingModelDO thingModel = IotThingModelConvert.INSTANCE.convert(updateReqVO); | ||
| 88 | + thingModelMapper.updateById(thingModel); | ||
| 89 | + | ||
| 90 | + // 5. 如果更新的是属性,需要更新默认的事件和服务 | ||
| 91 | + if (Objects.equals(updateReqVO.getType(), IotThingModelTypeEnum.PROPERTY.getType())) { | ||
| 92 | + createDefaultEventsAndServices(updateReqVO.getProductId(), updateReqVO.getProductKey()); | ||
| 93 | + } | ||
| 94 | + } | ||
| 95 | + | ||
| 96 | + @Override | ||
| 97 | + @Transactional(rollbackFor = Exception.class) | ||
| 98 | + public void deleteThingModel(Long id) { | ||
| 99 | + // 1. 校验功能是否存在 | ||
| 100 | + IotThingModelDO thingModel = thingModelMapper.selectById(id); | ||
| 101 | + if (thingModel == null) { | ||
| 102 | + throw exception(THING_MODEL_NOT_EXISTS); | ||
| 103 | + } | ||
| 104 | + | ||
| 105 | + // 3. 校验产品状态,发布状态下,不允许操作功能 | ||
| 106 | + validateProductStatus(thingModel.getProductId()); | ||
| 107 | + | ||
| 108 | + // 2. 删除功能 | ||
| 109 | + thingModelMapper.deleteById(id); | ||
| 110 | + | ||
| 111 | + // 3. 如果删除的是属性,需要更新默认的事件和服务 | ||
| 112 | + if (Objects.equals(thingModel.getType(), IotThingModelTypeEnum.PROPERTY.getType())) { | ||
| 113 | + createDefaultEventsAndServices(thingModel.getProductId(), thingModel.getProductKey()); | ||
| 114 | + } | ||
| 115 | + } | ||
| 116 | + | ||
| 117 | + @Override | ||
| 118 | + public IotThingModelDO getThingModel(Long id) { | ||
| 119 | + return thingModelMapper.selectById(id); | ||
| 120 | + } | ||
| 121 | + | ||
| 122 | + @Override | ||
| 123 | + public List<IotThingModelDO> getThingModelListByProductId(Long productId) { | ||
| 124 | + return thingModelMapper.selectListByProductId(productId); | ||
| 125 | + } | ||
| 126 | + | ||
| 127 | + @Override | ||
| 128 | + public PageResult<IotThingModelDO> getProductThingModelPage(IotThingModelPageReqVO pageReqVO) { | ||
| 129 | + return thingModelMapper.selectPage(pageReqVO); | ||
| 130 | + } | ||
| 131 | + | ||
| 132 | + @Override | ||
| 133 | + public List<IotThingModelDO> getProductThingModelListByProductKey(String productKey) { | ||
| 134 | + return thingModelMapper.selectListByProductKey(productKey); | ||
| 135 | + } | ||
| 136 | + | ||
| 137 | + /** | ||
| 138 | + * 校验功能是否存在 | ||
| 139 | + * | ||
| 140 | + * @param id 功能编号 | ||
| 141 | + */ | ||
| 142 | + private void validateProductThingModelMapperExists(Long id) { | ||
| 143 | + if (thingModelMapper.selectById(id) == null) { | ||
| 144 | + throw exception(THING_MODEL_NOT_EXISTS); | ||
| 145 | + } | ||
| 146 | + } | ||
| 147 | + | ||
| 148 | + private void validateIdentifierUniqueForUpdate(Long id, Long productId, String identifier) { | ||
| 149 | + IotThingModelDO thingModel = thingModelMapper.selectByProductIdAndIdentifier(productId, identifier); | ||
| 150 | + if (thingModel != null && ObjectUtil.notEqual(thingModel.getId(), id)) { | ||
| 151 | + throw exception(THING_MODEL_IDENTIFIER_EXISTS); | ||
| 152 | + } | ||
| 153 | + } | ||
| 154 | + | ||
| 155 | + private void validateProductStatus(Long createReqVO) { | ||
| 156 | + IotProductDO product = productService.getProduct(createReqVO); | ||
| 157 | + if (Objects.equals(product.getStatus(), IotProductStatusEnum.PUBLISHED.getStatus())) { | ||
| 158 | + throw exception(PRODUCT_STATUS_NOT_ALLOW_THING_MODEL); | ||
| 159 | + } | ||
| 160 | + } | ||
| 161 | + | ||
| 162 | + private void validateNotDefaultEventAndService(String identifier) { | ||
| 163 | + // set, get, post, property, event, time, value 是系统保留字段,不能用于标识符定义 | ||
| 164 | + if (CollUtil.containsAny(Arrays.asList("set", "get", "post", "property", "event", "time", "value"), Collections.singletonList(identifier))) { | ||
| 165 | + throw exception(THING_MODEL_IDENTIFIER_INVALID); | ||
| 166 | + } | ||
| 167 | + } | ||
| 168 | + | ||
| 169 | + private void validateNameUnique(Long productId, String name) { | ||
| 170 | + IotThingModelDO thingModel = thingModelMapper.selectByProductIdAndName(productId, name); | ||
| 171 | + if (thingModel != null) { | ||
| 172 | + throw exception(THING_MODEL_NAME_EXISTS); | ||
| 173 | + } | ||
| 174 | + } | ||
| 175 | + | ||
| 176 | + private void validateIdentifierUnique(Long productId, String identifier) { | ||
| 177 | + IotThingModelDO thingModel = thingModelMapper.selectByProductIdAndIdentifier(productId, identifier); | ||
| 178 | + if (thingModel != null) { | ||
| 179 | + throw exception(THING_MODEL_IDENTIFIER_EXISTS); | ||
| 180 | + } | ||
| 181 | + } | ||
| 182 | + | ||
| 183 | + /** | ||
| 184 | + * 创建默认的事件和服务 | ||
| 185 | + */ | ||
| 186 | + public void createDefaultEventsAndServices(Long productId, String productKey) { | ||
| 187 | + // 1. 获取当前属性列表 | ||
| 188 | + List<IotThingModelDO> properties = thingModelMapper | ||
| 189 | + .selectListByProductIdAndType(productId, IotThingModelTypeEnum.PROPERTY.getType()); | ||
| 190 | + | ||
| 191 | + // 2. 生成新的事件和服务列表 | ||
| 192 | + List<IotThingModelDO> newThingModels = new ArrayList<>(); | ||
| 193 | + // 2.1 生成属性上报事件 | ||
| 194 | + ThingModelEvent propertyPostEvent = generatePropertyPostEvent(properties); | ||
| 195 | + if (propertyPostEvent != null) { | ||
| 196 | + newThingModels.add(buildEventThingModelDO(productId, productKey, propertyPostEvent, "属性上报事件")); | ||
| 197 | + } | ||
| 198 | + // 2.2 生成属性设置服务 | ||
| 199 | + ThingModelService propertySetService = generatePropertySetService(properties); | ||
| 200 | + if (propertySetService != null) { | ||
| 201 | + newThingModels.add(buildServiceThingModelDO(productId, productKey, propertySetService, "属性设置服务")); | ||
| 202 | + } | ||
| 203 | + // 2.3 生成属性获取服务 | ||
| 204 | + ThingModelService propertyGetService = generatePropertyGetService(properties); | ||
| 205 | + if (propertyGetService != null) { | ||
| 206 | + newThingModels.add(buildServiceThingModelDO(productId, productKey, propertyGetService, "属性获取服务")); | ||
| 207 | + } | ||
| 208 | + | ||
| 209 | + // 3.1 获取数据库中的默认的旧事件和服务列表 | ||
| 210 | + List<IotThingModelDO> oldThingModels = thingModelMapper.selectListByProductIdAndIdentifiersAndTypes( | ||
| 211 | + productId, | ||
| 212 | + Arrays.asList("post", "set", "get"), | ||
| 213 | + Arrays.asList(IotThingModelTypeEnum.EVENT.getType(), IotThingModelTypeEnum.SERVICE.getType()) | ||
| 214 | + ); | ||
| 215 | + // 3.2 创建默认的事件和服务 | ||
| 216 | + createDefaultEventsAndServices(oldThingModels, newThingModels); | ||
| 217 | + } | ||
| 218 | + | ||
| 219 | + /** | ||
| 220 | + * 创建默认的事件和服务 | ||
| 221 | + */ | ||
| 222 | + private void createDefaultEventsAndServices(List<IotThingModelDO> oldThingModels, | ||
| 223 | + List<IotThingModelDO> newThingModels) { | ||
| 224 | + // 使用 diffList 方法比较新旧列表 | ||
| 225 | + List<List<IotThingModelDO>> diffResult = diffList(oldThingModels, newThingModels, | ||
| 226 | + (oldVal, newVal) -> { | ||
| 227 | + // 继续使用 identifier 和 type 进行比较:这样可以准确地匹配对应的功能对象。 | ||
| 228 | + boolean same = Objects.equals(oldVal.getIdentifier(), newVal.getIdentifier()) | ||
| 229 | + && Objects.equals(oldVal.getType(), newVal.getType()); | ||
| 230 | + if (same) { | ||
| 231 | + newVal.setId(oldVal.getId()); // 设置编号 | ||
| 232 | + } | ||
| 233 | + return same; | ||
| 234 | + }); | ||
| 235 | + // 批量添加、修改、删除 | ||
| 236 | + if (CollUtil.isNotEmpty(diffResult.get(0))) { | ||
| 237 | + thingModelMapper.insertBatch(diffResult.get(0)); | ||
| 238 | + } | ||
| 239 | + if (CollUtil.isNotEmpty(diffResult.get(1))) { | ||
| 240 | + thingModelMapper.updateBatch(diffResult.get(1)); | ||
| 241 | + } | ||
| 242 | + if (CollUtil.isNotEmpty(diffResult.get(2))) { | ||
| 243 | + thingModelMapper.deleteByIds(convertSet(diffResult.get(2), IotThingModelDO::getId)); | ||
| 244 | + } | ||
| 245 | + } | ||
| 246 | + | ||
| 247 | + /** | ||
| 248 | + * 构建事件功能对象 | ||
| 249 | + */ | ||
| 250 | + private IotThingModelDO buildEventThingModelDO(Long productId, String productKey, | ||
| 251 | + ThingModelEvent event, String description) { | ||
| 252 | + return new IotThingModelDO().setProductId(productId).setProductKey(productKey) | ||
| 253 | + .setIdentifier(event.getIdentifier()).setName(event.getName()).setDescription(description) | ||
| 254 | + .setType(IotThingModelTypeEnum.EVENT.getType()).setEvent(event); | ||
| 255 | + } | ||
| 256 | + | ||
| 257 | + /** | ||
| 258 | + * 构建服务功能对象 | ||
| 259 | + */ | ||
| 260 | + private IotThingModelDO buildServiceThingModelDO(Long productId, String productKey, | ||
| 261 | + ThingModelService service, String description) { | ||
| 262 | + return new IotThingModelDO().setProductId(productId).setProductKey(productKey) | ||
| 263 | + .setIdentifier(service.getIdentifier()).setName(service.getName()).setDescription(description) | ||
| 264 | + .setType(IotThingModelTypeEnum.SERVICE.getType()).setService(service); | ||
| 265 | + } | ||
| 266 | + | ||
| 267 | + /** | ||
| 268 | + * 生成属性上报事件 | ||
| 269 | + */ | ||
| 270 | + private ThingModelEvent generatePropertyPostEvent(List<IotThingModelDO> thingModels) { | ||
| 271 | + // 没有属性则不生成 | ||
| 272 | + if (CollUtil.isEmpty(thingModels)) { | ||
| 273 | + return null; | ||
| 274 | + } | ||
| 275 | + | ||
| 276 | + // 生成属性上报事件 | ||
| 277 | + return new ThingModelEvent().setIdentifier("post").setName("属性上报").setMethod("thing.event.property.post") | ||
| 278 | + .setType(IotThingModelServiceEventTypeEnum.INFO.getType()) | ||
| 279 | + .setOutputParams(buildInputOutputParam(thingModels, IotThingModelParamDirectionEnum.OUTPUT)); | ||
| 280 | + } | ||
| 281 | + | ||
| 282 | + /** | ||
| 283 | + * 生成属性设置服务 | ||
| 284 | + */ | ||
| 285 | + private ThingModelService generatePropertySetService(List<IotThingModelDO> thingModels) { | ||
| 286 | + // 1.1 过滤出所有可写属性 | ||
| 287 | + thingModels = filterList(thingModels, thingModel -> | ||
| 288 | + IotThingModelAccessModeEnum.READ_WRITE.getMode().equals(thingModel.getProperty().getAccessMode())); | ||
| 289 | + // 1.2 没有可写属性则不生成 | ||
| 290 | + if (CollUtil.isEmpty(thingModels)) { | ||
| 291 | + return null; | ||
| 292 | + } | ||
| 293 | + | ||
| 294 | + // 2. 生成属性设置服务 | ||
| 295 | + return new ThingModelService().setIdentifier("set").setName("属性设置").setMethod("thing.service.property.set") | ||
| 296 | + .setCallType(IotThingModelServiceCallTypeEnum.ASYNC.getType()) | ||
| 297 | + .setInputParams(buildInputOutputParam(thingModels, IotThingModelParamDirectionEnum.INPUT)) | ||
| 298 | + .setOutputParams(Collections.emptyList()); // 属性设置服务一般不需要输出参数 | ||
| 299 | + } | ||
| 300 | + | ||
| 301 | + /** | ||
| 302 | + * 生成属性获取服务 | ||
| 303 | + */ | ||
| 304 | + private ThingModelService generatePropertyGetService(List<IotThingModelDO> thingModels) { | ||
| 305 | + // 1.1 没有属性则不生成 | ||
| 306 | + if (CollUtil.isEmpty(thingModels)) { | ||
| 307 | + return null; | ||
| 308 | + } | ||
| 309 | + | ||
| 310 | + // 1.2 生成属性获取服务 | ||
| 311 | + return new ThingModelService().setIdentifier("get").setName("属性获取").setMethod("thing.service.property.get") | ||
| 312 | + .setCallType(IotThingModelServiceCallTypeEnum.ASYNC.getType()) | ||
| 313 | + .setInputParams(buildInputOutputParam(thingModels, IotThingModelParamDirectionEnum.INPUT)) | ||
| 314 | + .setOutputParams(buildInputOutputParam(thingModels, IotThingModelParamDirectionEnum.OUTPUT)); | ||
| 315 | + } | ||
| 316 | + | ||
| 317 | + /** | ||
| 318 | + * 构建输入/输出参数列表 | ||
| 319 | + * | ||
| 320 | + * @param thingModels 属性列表 | ||
| 321 | + * @return 输入/输出参数列表 | ||
| 322 | + */ | ||
| 323 | + private List<ThingModelParam> buildInputOutputParam(List<IotThingModelDO> thingModels, | ||
| 324 | + IotThingModelParamDirectionEnum direction) { | ||
| 325 | + return convertList(thingModels, thingModel -> | ||
| 326 | + BeanUtils.toBean(thingModel.getProperty(), ThingModelParam.class).setParaOrder(0) // TODO @puhui999: 先搞个默认值看看怎么个事 | ||
| 327 | + .setDirection(direction.getDirection())); | ||
| 328 | + } | ||
| 329 | + | ||
| 330 | +} |
yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/util/IotTdDatabaseUtils.java
| 1 | package cn.iocoder.yudao.module.iot.util; | 1 | package cn.iocoder.yudao.module.iot.util; |
| 2 | 2 | ||
| 3 | +import cn.hutool.core.lang.Assert; | ||
| 3 | import cn.hutool.core.util.StrUtil; | 4 | import cn.hutool.core.util.StrUtil; |
| 4 | import cn.iocoder.yudao.module.iot.enums.IotConstants; | 5 | import cn.iocoder.yudao.module.iot.enums.IotConstants; |
| 5 | import cn.iocoder.yudao.module.iot.enums.product.IotProductDeviceTypeEnum; | 6 | import cn.iocoder.yudao.module.iot.enums.product.IotProductDeviceTypeEnum; |
| 6 | -import org.springframework.beans.factory.annotation.Value; | ||
| 7 | -import org.springframework.stereotype.Component; | ||
| 8 | - | ||
| 9 | -import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception; | ||
| 10 | -import static cn.iocoder.yudao.module.iot.enums.ErrorCodeConstants.PRODUCT_DEVICE_NOT_EXISTS; | ||
| 11 | 7 | ||
| 12 | // TODO @芋艿:可能要思索下,有没更好的处理方式 | 8 | // TODO @芋艿:可能要思索下,有没更好的处理方式 |
| 13 | // TODO @芋艿:怎么改成无状态 | 9 | // TODO @芋艿:怎么改成无状态 |
| @@ -34,20 +30,17 @@ public class IotTdDatabaseUtils { | @@ -34,20 +30,17 @@ public class IotTdDatabaseUtils { | ||
| 34 | * @return 产品超级表表名 | 30 | * @return 产品超级表表名 |
| 35 | */ | 31 | */ |
| 36 | public static String getProductSuperTableName(Integer deviceType, String productKey) { | 32 | public static String getProductSuperTableName(Integer deviceType, String productKey) { |
| 37 | - // TODO @alwayssuper:枚举字段,不要 1、2、3;不符合预期,抛出异常 | ||
| 38 | - if (deviceType == null) { | ||
| 39 | - throw exception(PRODUCT_DEVICE_NOT_EXISTS); | ||
| 40 | - } | 33 | + Assert.notNull(deviceType, "deviceType 不能为空"); |
| 41 | if (IotProductDeviceTypeEnum.GATEWAY_SUB.getType().equals(deviceType)) { | 34 | if (IotProductDeviceTypeEnum.GATEWAY_SUB.getType().equals(deviceType)) { |
| 42 | return String.format(IotConstants.GATEWAY_SUB_STABLE_NAME_FORMAT, productKey).toLowerCase(); | 35 | return String.format(IotConstants.GATEWAY_SUB_STABLE_NAME_FORMAT, productKey).toLowerCase(); |
| 43 | - } else if (IotProductDeviceTypeEnum.GATEWAY.getType().equals(deviceType)) { | 36 | + } |
| 37 | + if (IotProductDeviceTypeEnum.GATEWAY.getType().equals(deviceType)) { | ||
| 44 | return String.format(IotConstants.GATEWAY_STABLE_NAME_FORMAT, productKey).toLowerCase(); | 38 | return String.format(IotConstants.GATEWAY_STABLE_NAME_FORMAT, productKey).toLowerCase(); |
| 45 | - } else if (IotProductDeviceTypeEnum.DIRECT.getType().equals(deviceType)){ | ||
| 46 | - return String.format(IotConstants.DEVICE_STABLE_NAME_FORMAT, productKey).toLowerCase(); | ||
| 47 | } | 39 | } |
| 48 | - else{ | ||
| 49 | - throw exception(PRODUCT_DEVICE_NOT_EXISTS); | 40 | + if (IotProductDeviceTypeEnum.DIRECT.getType().equals(deviceType)){ |
| 41 | + return String.format(IotConstants.DEVICE_STABLE_NAME_FORMAT, productKey).toLowerCase(); | ||
| 50 | } | 42 | } |
| 43 | + throw new IllegalArgumentException("deviceType 不正确"); | ||
| 51 | } | 44 | } |
| 52 | 45 | ||
| 53 | /** | 46 | /** |
| @@ -58,7 +51,6 @@ public class IotTdDatabaseUtils { | @@ -58,7 +51,6 @@ public class IotTdDatabaseUtils { | ||
| 58 | * | 51 | * |
| 59 | */ | 52 | */ |
| 60 | public static String getThingModelMessageSuperTableName(String productKey) { | 53 | public static String getThingModelMessageSuperTableName(String productKey) { |
| 61 | - // TODO @alwayssuper:是不是应该 + 拼接就好,不用 format | ||
| 62 | return "thing_model_message_" + productKey.toLowerCase(); | 54 | return "thing_model_message_" + productKey.toLowerCase(); |
| 63 | } | 55 | } |
| 64 | 56 | ||
| @@ -70,7 +62,8 @@ public class IotTdDatabaseUtils { | @@ -70,7 +62,8 @@ public class IotTdDatabaseUtils { | ||
| 70 | * @return 物模型日志设备表名 | 62 | * @return 物模型日志设备表名 |
| 71 | */ | 63 | */ |
| 72 | public static String getThingModelMessageDeviceTableName(String productKey, String deviceName) { | 64 | public static String getThingModelMessageDeviceTableName(String productKey, String deviceName) { |
| 73 | - return String.format(IotConstants.THING_MODEL_MESSAGE_TABLE_NAME_FORMAT, productKey.toLowerCase(), deviceName.toLowerCase()); | 65 | + return String.format(IotConstants.THING_MODEL_MESSAGE_TABLE_NAME_FORMAT, |
| 66 | + productKey.toLowerCase(), deviceName.toLowerCase()); | ||
| 74 | } | 67 | } |
| 75 | 68 | ||
| 76 | } | 69 | } |
yudao-module-iot/yudao-module-iot-biz/src/main/resources/mapper/device/IotDevicePropertyDataMapper.xml
0 → 100644
| 1 | +<?xml version="1.0" encoding="UTF-8"?> | ||
| 2 | +<!DOCTYPE mapper | ||
| 3 | + PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" | ||
| 4 | + "http://mybatis.org/dtd/mybatis-3-mapper.dtd"> | ||
| 5 | +<mapper namespace="cn.iocoder.yudao.module.iot.dal.tdengine.IotDevicePropertyDataMapper"> | ||
| 6 | + | ||
| 7 | + <select id="getProductPropertySTableFieldList" resultType="cn.iocoder.yudao.module.iot.framework.tdengine.core.TDengineTableField"> | ||
| 8 | + DESCRIBE product_property_${productKey} | ||
| 9 | + </select> | ||
| 10 | + | ||
| 11 | + <update id="createProductPropertySTable"> | ||
| 12 | + CREATE STABLE product_property_${productKey} | ||
| 13 | + <foreach item="field" collection="fields" separator="," open="(" close=")"> | ||
| 14 | + ${field.field} ${field.type} | ||
| 15 | + <if test="field.length != null and field.length > 0"> | ||
| 16 | + (${field.length}) | ||
| 17 | + </if> | ||
| 18 | + </foreach> | ||
| 19 | + TAGS ( | ||
| 20 | + device_key NCHAR(50) | ||
| 21 | + ) | ||
| 22 | + </update> | ||
| 23 | + | ||
| 24 | + <update id="alterProductPropertySTableAddField"> | ||
| 25 | + ALTER STABLE product_property_${productKey} | ||
| 26 | + ADD COLUMN ${field.field} ${field.type} | ||
| 27 | + <if test="field.length != null and field.length > 0"> | ||
| 28 | + (${field.length}) | ||
| 29 | + </if> | ||
| 30 | + </update> | ||
| 31 | + | ||
| 32 | + <update id="alterProductPropertySTableModifyField"> | ||
| 33 | + ALTER STABLE product_property_${productKey} | ||
| 34 | + MODIFY COLUMN ${field.field} ${field.type} | ||
| 35 | + <if test="field.length != null and field.length > 0"> | ||
| 36 | + (${field.length}) | ||
| 37 | + </if> | ||
| 38 | + </update> | ||
| 39 | + | ||
| 40 | + <update id="alterProductPropertySTableDropField"> | ||
| 41 | + ALTER STABLE product_property_${productKey} | ||
| 42 | + DROP COLUMN ${field.field} | ||
| 43 | + </update> | ||
| 44 | + | ||
| 45 | +</mapper> | ||
| 0 | \ No newline at end of file | 46 | \ No newline at end of file |
yudao-module-iot/yudao-module-iot-biz/src/main/resources/mapper/plugininstance/PluginInstanceMapper.xml
| 1 | <?xml version="1.0" encoding="UTF-8"?> | 1 | <?xml version="1.0" encoding="UTF-8"?> |
| 2 | <!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd"> | 2 | <!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd"> |
| 3 | -<mapper namespace="cn.iocoder.yudao.module.iot.dal.mysql.plugininstance.PluginInstanceMapper"> | 3 | +<mapper namespace="cn.iocoder.yudao.module.iot.dal.mysql.plugin.PluginInstanceMapper"> |
| 4 | 4 | ||
| 5 | <!-- | 5 | <!-- |
| 6 | 一般情况下,尽可能使用 Mapper 进行 CRUD 增删改查即可。 | 6 | 一般情况下,尽可能使用 Mapper 进行 CRUD 增删改查即可。 |
yudao-module-iot/yudao-module-iot-biz/src/main/resources/mapper/tdengine/TdThinkModelMessageMapper.xml
| @@ -5,7 +5,6 @@ | @@ -5,7 +5,6 @@ | ||
| 5 | <mapper namespace="cn.iocoder.yudao.module.iot.dal.tdengine.TdThingModelMessageMapper"> | 5 | <mapper namespace="cn.iocoder.yudao.module.iot.dal.tdengine.TdThingModelMessageMapper"> |
| 6 | 6 | ||
| 7 | <!-- 创建物模型消息日志超级表 --> | 7 | <!-- 创建物模型消息日志超级表 --> |
| 8 | - <!-- TODO @芋艿:捉摸下字段,特别是 sys、ts 这种缩写 --> | ||
| 9 | <update id="createSuperTable"> | 8 | <update id="createSuperTable"> |
| 10 | CREATE STABLE ${dataBaseName}.${superTableName}( | 9 | CREATE STABLE ${dataBaseName}.${superTableName}( |
| 11 | ts TIMESTAMP, | 10 | ts TIMESTAMP, |
yudao-module-iot/yudao-module-iot-plugin/yudao-module-iot-http-plugin/pom.xml
| @@ -24,6 +24,7 @@ | @@ -24,6 +24,7 @@ | ||
| 24 | <plugin.class>cn.iocoder.yudao.module.iot.plugin.HttpPlugin</plugin.class> | 24 | <plugin.class>cn.iocoder.yudao.module.iot.plugin.HttpPlugin</plugin.class> |
| 25 | <plugin.version>0.0.1</plugin.version> | 25 | <plugin.version>0.0.1</plugin.version> |
| 26 | <plugin.provider>ahh</plugin.provider> | 26 | <plugin.provider>ahh</plugin.provider> |
| 27 | + <plugin.description>http-plugin-0.0.1</plugin.description> | ||
| 27 | <plugin.dependencies/> | 28 | <plugin.dependencies/> |
| 28 | </properties> | 29 | </properties> |
| 29 | 30 | ||
| @@ -104,6 +105,7 @@ | @@ -104,6 +105,7 @@ | ||
| 104 | <Plugin-Class>${plugin.class}</Plugin-Class> | 105 | <Plugin-Class>${plugin.class}</Plugin-Class> |
| 105 | <Plugin-Version>${plugin.version}</Plugin-Version> | 106 | <Plugin-Version>${plugin.version}</Plugin-Version> |
| 106 | <Plugin-Provider>${plugin.provider}</Plugin-Provider> | 107 | <Plugin-Provider>${plugin.provider}</Plugin-Provider> |
| 108 | + <Plugin-Description>${plugin.description}</Plugin-Description> | ||
| 107 | <Plugin-Dependencies>${plugin.dependencies}</Plugin-Dependencies> | 109 | <Plugin-Dependencies>${plugin.dependencies}</Plugin-Dependencies> |
| 108 | </manifestEntries> | 110 | </manifestEntries> |
| 109 | </archive> | 111 | </archive> |
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; | @@ -2,7 +2,7 @@ package cn.iocoder.yudao.module.iot.plugin; | ||
| 2 | 2 | ||
| 3 | import cn.hutool.json.JSONObject; | 3 | import cn.hutool.json.JSONObject; |
| 4 | import cn.hutool.json.JSONUtil; | 4 | import cn.hutool.json.JSONUtil; |
| 5 | -import cn.iocoder.yudao.module.iot.api.DeviceDataApi; | 5 | +import cn.iocoder.yudao.module.iot.api.device.DeviceDataApi; |
| 6 | import io.netty.buffer.Unpooled; | 6 | import io.netty.buffer.Unpooled; |
| 7 | import io.netty.channel.ChannelFutureListener; | 7 | import io.netty.channel.ChannelFutureListener; |
| 8 | import io.netty.channel.ChannelHandlerContext; | 8 | import io.netty.channel.ChannelHandlerContext; |
| @@ -12,12 +12,9 @@ import io.netty.util.CharsetUtil; | @@ -12,12 +12,9 @@ import io.netty.util.CharsetUtil; | ||
| 12 | 12 | ||
| 13 | /** | 13 | /** |
| 14 | * 基于 Netty 的 HTTP 处理器,用于接收设备上报的数据并调用主程序的 DeviceDataApi 接口进行处理。 | 14 | * 基于 Netty 的 HTTP 处理器,用于接收设备上报的数据并调用主程序的 DeviceDataApi 接口进行处理。 |
| 15 | - * <p> | ||
| 16 | - * 请求格式: | ||
| 17 | - * POST /sys/{productKey}/{deviceName}/thing/event/property/post | ||
| 18 | - * 请求体为 JSON 格式数据。 | ||
| 19 | - * <p> | ||
| 20 | - * 返回结果为 JSON 格式,包含统一的 code、data、id、message、method、version 字段。 | 15 | + * |
| 16 | + * 1. 请求格式:JSON 格式,地址为 POST /sys/{productKey}/{deviceName}/thing/event/property/post | ||
| 17 | + * 2. 返回结果:JSON 格式,包含统一的 code、data、id、message、method、version 字段 | ||
| 21 | */ | 18 | */ |
| 22 | public class HttpHandler extends SimpleChannelInboundHandler<FullHttpRequest> { | 19 | public class HttpHandler extends SimpleChannelInboundHandler<FullHttpRequest> { |
| 23 | 20 | ||
| @@ -29,10 +26,9 @@ public class HttpHandler extends SimpleChannelInboundHandler<FullHttpRequest> { | @@ -29,10 +26,9 @@ public class HttpHandler extends SimpleChannelInboundHandler<FullHttpRequest> { | ||
| 29 | 26 | ||
| 30 | @Override | 27 | @Override |
| 31 | protected void channelRead0(ChannelHandlerContext ctx, FullHttpRequest request) { | 28 | protected void channelRead0(ChannelHandlerContext ctx, FullHttpRequest request) { |
| 32 | - String uri = request.uri(); | ||
| 33 | - | ||
| 34 | // 期望的路径格式: /sys/{productKey}/{deviceName}/thing/event/property/post | 29 | // 期望的路径格式: /sys/{productKey}/{deviceName}/thing/event/property/post |
| 35 | // 使用 "/" 拆分路径 | 30 | // 使用 "/" 拆分路径 |
| 31 | + String uri = request.uri(); | ||
| 36 | String[] parts = uri.split("/"); | 32 | String[] parts = uri.split("/"); |
| 37 | 33 | ||
| 38 | /* | 34 | /* |
| @@ -52,25 +48,19 @@ public class HttpHandler extends SimpleChannelInboundHandler<FullHttpRequest> { | @@ -52,25 +48,19 @@ public class HttpHandler extends SimpleChannelInboundHandler<FullHttpRequest> { | ||
| 52 | && "event".equals(parts[5]) | 48 | && "event".equals(parts[5]) |
| 53 | && "property".equals(parts[6]) | 49 | && "property".equals(parts[6]) |
| 54 | && "post".equals(parts[7]); | 50 | && "post".equals(parts[7]); |
| 55 | - | ||
| 56 | if (!isCorrectPath) { | 51 | if (!isCorrectPath) { |
| 57 | - // 如果路径不匹配,返回 404 错误 | ||
| 58 | writeResponse(ctx, HttpResponseStatus.NOT_FOUND, "Not Found"); | 52 | writeResponse(ctx, HttpResponseStatus.NOT_FOUND, "Not Found"); |
| 59 | return; | 53 | return; |
| 60 | } | 54 | } |
| 61 | - | ||
| 62 | String productKey = parts[2]; | 55 | String productKey = parts[2]; |
| 63 | String deviceName = parts[3]; | 56 | String deviceName = parts[3]; |
| 64 | 57 | ||
| 65 | - // 从请求中获取原始数据 | 58 | + // 从请求中获取原始数据,尝试解析请求数据为 JSON 对象 |
| 66 | String requestBody = request.content().toString(CharsetUtil.UTF_8); | 59 | String requestBody = request.content().toString(CharsetUtil.UTF_8); |
| 67 | - | ||
| 68 | - // 尝试解析请求数据为 JSON 对象 | ||
| 69 | JSONObject jsonData; | 60 | JSONObject jsonData; |
| 70 | try { | 61 | try { |
| 71 | jsonData = JSONUtil.parseObj(requestBody); | 62 | jsonData = JSONUtil.parseObj(requestBody); |
| 72 | } catch (Exception e) { | 63 | } catch (Exception e) { |
| 73 | - // 数据不是合法的 JSON 格式,返回 400 错误 | ||
| 74 | JSONObject res = createResponseJson( | 64 | JSONObject res = createResponseJson( |
| 75 | 400, | 65 | 400, |
| 76 | new JSONObject(), | 66 | new JSONObject(), |
| @@ -82,8 +72,6 @@ public class HttpHandler extends SimpleChannelInboundHandler<FullHttpRequest> { | @@ -82,8 +72,6 @@ public class HttpHandler extends SimpleChannelInboundHandler<FullHttpRequest> { | ||
| 82 | writeResponse(ctx, HttpResponseStatus.BAD_REQUEST, res.toString()); | 72 | writeResponse(ctx, HttpResponseStatus.BAD_REQUEST, res.toString()); |
| 83 | return; | 73 | return; |
| 84 | } | 74 | } |
| 85 | - | ||
| 86 | - // 获取请求中的 id 字段,若不存在则为 null | ||
| 87 | String id = jsonData.getStr("id", null); | 75 | String id = jsonData.getStr("id", null); |
| 88 | 76 | ||
| 89 | try { | 77 | try { |
| @@ -101,7 +89,6 @@ public class HttpHandler extends SimpleChannelInboundHandler<FullHttpRequest> { | @@ -101,7 +89,6 @@ public class HttpHandler extends SimpleChannelInboundHandler<FullHttpRequest> { | ||
| 101 | ); | 89 | ); |
| 102 | writeResponse(ctx, HttpResponseStatus.OK, successRes.toString()); | 90 | writeResponse(ctx, HttpResponseStatus.OK, successRes.toString()); |
| 103 | } catch (Exception e) { | 91 | } catch (Exception e) { |
| 104 | - // 保存数据过程中出现异常,返回 500 错误 | ||
| 105 | JSONObject errorRes = createResponseJson( | 92 | JSONObject errorRes = createResponseJson( |
| 106 | 500, | 93 | 500, |
| 107 | new JSONObject(), | 94 | new JSONObject(), |
| @@ -128,7 +115,7 @@ public class HttpHandler extends SimpleChannelInboundHandler<FullHttpRequest> { | @@ -128,7 +115,7 @@ public class HttpHandler extends SimpleChannelInboundHandler<FullHttpRequest> { | ||
| 128 | private JSONObject createResponseJson(int code, JSONObject data, String id, String message, String method, String version) { | 115 | private JSONObject createResponseJson(int code, JSONObject data, String id, String message, String method, String version) { |
| 129 | JSONObject res = new JSONObject(); | 116 | JSONObject res = new JSONObject(); |
| 130 | res.set("code", code); | 117 | res.set("code", code); |
| 131 | - res.set("data", data != null ? data : new JSONObject()); // 确保 data 不为 null | 118 | + res.set("data", data != null ? data : new JSONObject()); |
| 132 | res.set("id", id); | 119 | res.set("id", id); |
| 133 | res.set("message", message); | 120 | res.set("message", message); |
| 134 | res.set("method", method); | 121 | res.set("method", method); |
| @@ -137,24 +124,24 @@ public class HttpHandler extends SimpleChannelInboundHandler<FullHttpRequest> { | @@ -137,24 +124,24 @@ public class HttpHandler extends SimpleChannelInboundHandler<FullHttpRequest> { | ||
| 137 | } | 124 | } |
| 138 | 125 | ||
| 139 | /** | 126 | /** |
| 140 | - * 向客户端返回 HTTP 响应的辅助方法。 | 127 | + * 向客户端返回 HTTP 响应的辅助方法 |
| 141 | * | 128 | * |
| 142 | * @param ctx 通道上下文 | 129 | * @param ctx 通道上下文 |
| 143 | * @param status HTTP 响应状态码(网络层面的) | 130 | * @param status HTTP 响应状态码(网络层面的) |
| 144 | * @param content 响应内容(JSON 字符串或其他文本) | 131 | * @param content 响应内容(JSON 字符串或其他文本) |
| 145 | */ | 132 | */ |
| 146 | private void writeResponse(ChannelHandlerContext ctx, HttpResponseStatus status, String content) { | 133 | private void writeResponse(ChannelHandlerContext ctx, HttpResponseStatus status, String content) { |
| 134 | + // 设置响应头为 JSON 类型和正确的编码 | ||
| 147 | FullHttpResponse response = new DefaultFullHttpResponse( | 135 | FullHttpResponse response = new DefaultFullHttpResponse( |
| 148 | HttpVersion.HTTP_1_1, | 136 | HttpVersion.HTTP_1_1, |
| 149 | status, | 137 | status, |
| 150 | Unpooled.copiedBuffer(content, CharsetUtil.UTF_8) | 138 | Unpooled.copiedBuffer(content, CharsetUtil.UTF_8) |
| 151 | ); | 139 | ); |
| 152 | - | ||
| 153 | - // 设置响应头为 JSON 类型和正确的编码 | ||
| 154 | response.headers().set(HttpHeaderNames.CONTENT_TYPE, "application/json; charset=UTF-8"); | 140 | response.headers().set(HttpHeaderNames.CONTENT_TYPE, "application/json; charset=UTF-8"); |
| 155 | response.headers().set(HttpHeaderNames.CONTENT_LENGTH, response.content().readableBytes()); | 141 | response.headers().set(HttpHeaderNames.CONTENT_LENGTH, response.content().readableBytes()); |
| 156 | 142 | ||
| 157 | // 发送响应并在发送完成后关闭连接 | 143 | // 发送响应并在发送完成后关闭连接 |
| 158 | ctx.writeAndFlush(response).addListener(ChannelFutureListener.CLOSE); | 144 | ctx.writeAndFlush(response).addListener(ChannelFutureListener.CLOSE); |
| 159 | } | 145 | } |
| 146 | + | ||
| 160 | } | 147 | } |
| 161 | \ No newline at end of file | 148 | \ No newline at end of file |
yudao-module-iot/yudao-module-iot-plugin/yudao-module-iot-http-plugin/src/main/java/cn/iocoder/yudao/module/iot/plugin/HttpPlugin.java
| 1 | package cn.iocoder.yudao.module.iot.plugin; | 1 | package cn.iocoder.yudao.module.iot.plugin; |
| 2 | 2 | ||
| 3 | -import cn.iocoder.yudao.module.iot.api.DeviceDataApi; | 3 | +import cn.iocoder.yudao.module.iot.api.device.DeviceDataApi; |
| 4 | import cn.iocoder.yudao.module.iot.api.ServiceRegistry; | 4 | import cn.iocoder.yudao.module.iot.api.ServiceRegistry; |
| 5 | import io.netty.bootstrap.ServerBootstrap; | 5 | import io.netty.bootstrap.ServerBootstrap; |
| 6 | import io.netty.channel.*; | 6 | import io.netty.channel.*; |
| @@ -18,18 +18,25 @@ import java.util.concurrent.Executors; | @@ -18,18 +18,25 @@ import java.util.concurrent.Executors; | ||
| 18 | public class HttpPlugin extends Plugin { | 18 | public class HttpPlugin extends Plugin { |
| 19 | 19 | ||
| 20 | private static final int PORT = 8092; | 20 | private static final int PORT = 8092; |
| 21 | - private final ExecutorService executorService; | ||
| 22 | - private DeviceDataApi deviceDataApi; // 用于保存设备数据的 API | 21 | + |
| 22 | + private ExecutorService executorService; | ||
| 23 | + private DeviceDataApi deviceDataApi; | ||
| 23 | 24 | ||
| 24 | public HttpPlugin(PluginWrapper wrapper) { | 25 | public HttpPlugin(PluginWrapper wrapper) { |
| 25 | super(wrapper); | 26 | super(wrapper); |
| 26 | - this.executorService = Executors.newSingleThreadExecutor(); // 创建单线程池 | 27 | + // 初始化线程池 |
| 28 | + this.executorService = Executors.newSingleThreadExecutor(); | ||
| 27 | } | 29 | } |
| 28 | 30 | ||
| 29 | @Override | 31 | @Override |
| 30 | public void start() { | 32 | public void start() { |
| 31 | log.info("HttpPlugin.start()"); | 33 | log.info("HttpPlugin.start()"); |
| 32 | 34 | ||
| 35 | + // 重新初始化线程池,确保它是活跃的 | ||
| 36 | + if (executorService.isShutdown() || executorService.isTerminated()) { | ||
| 37 | + executorService = Executors.newSingleThreadExecutor(); | ||
| 38 | + } | ||
| 39 | + | ||
| 33 | // 从 ServiceRegistry 中获取主程序暴露的 DeviceDataApi 接口实例 | 40 | // 从 ServiceRegistry 中获取主程序暴露的 DeviceDataApi 接口实例 |
| 34 | deviceDataApi = ServiceRegistry.getService(DeviceDataApi.class); | 41 | deviceDataApi = ServiceRegistry.getService(DeviceDataApi.class); |
| 35 | if (deviceDataApi == null) { | 42 | if (deviceDataApi == null) { |
| @@ -44,10 +51,13 @@ public class HttpPlugin extends Plugin { | @@ -44,10 +51,13 @@ public class HttpPlugin extends Plugin { | ||
| 44 | @Override | 51 | @Override |
| 45 | public void stop() { | 52 | public void stop() { |
| 46 | log.info("HttpPlugin.stop()"); | 53 | log.info("HttpPlugin.stop()"); |
| 47 | - executorService.shutdownNow(); // 停止线程池 | 54 | + // 停止线程池 |
| 55 | + executorService.shutdownNow(); | ||
| 48 | } | 56 | } |
| 49 | 57 | ||
| 50 | - // 启动 HTTP 服务 | 58 | + /** |
| 59 | + * 启动 HTTP 服务 | ||
| 60 | + */ | ||
| 51 | private void startHttpServer() { | 61 | private void startHttpServer() { |
| 52 | EventLoopGroup bossGroup = new NioEventLoopGroup(1); | 62 | EventLoopGroup bossGroup = new NioEventLoopGroup(1); |
| 53 | EventLoopGroup workerGroup = new NioEventLoopGroup(); | 63 | EventLoopGroup workerGroup = new NioEventLoopGroup(); |
| @@ -56,7 +66,8 @@ public class HttpPlugin extends Plugin { | @@ -56,7 +66,8 @@ public class HttpPlugin extends Plugin { | ||
| 56 | ServerBootstrap bootstrap = new ServerBootstrap(); | 66 | ServerBootstrap bootstrap = new ServerBootstrap(); |
| 57 | bootstrap.group(bossGroup, workerGroup) | 67 | bootstrap.group(bossGroup, workerGroup) |
| 58 | .channel(NioServerSocketChannel.class) | 68 | .channel(NioServerSocketChannel.class) |
| 59 | - .childHandler(new ChannelInitializer<Channel>() { | 69 | + .childHandler(new ChannelInitializer<>() { |
| 70 | + | ||
| 60 | @Override | 71 | @Override |
| 61 | protected void initChannel(Channel channel) { | 72 | protected void initChannel(Channel channel) { |
| 62 | channel.pipeline().addLast(new HttpServerCodec()); | 73 | channel.pipeline().addLast(new HttpServerCodec()); |
| @@ -64,6 +75,7 @@ public class HttpPlugin extends Plugin { | @@ -64,6 +75,7 @@ public class HttpPlugin extends Plugin { | ||
| 64 | // 将从 ServiceRegistry 获取的 deviceDataApi 传入处理器 | 75 | // 将从 ServiceRegistry 获取的 deviceDataApi 传入处理器 |
| 65 | channel.pipeline().addLast(new HttpHandler(deviceDataApi)); | 76 | channel.pipeline().addLast(new HttpHandler(deviceDataApi)); |
| 66 | } | 77 | } |
| 78 | + | ||
| 67 | }); | 79 | }); |
| 68 | 80 | ||
| 69 | // 绑定端口并启动服务器 | 81 | // 绑定端口并启动服务器 |
| @@ -72,10 +84,11 @@ public class HttpPlugin extends Plugin { | @@ -72,10 +84,11 @@ public class HttpPlugin extends Plugin { | ||
| 72 | future.channel().closeFuture().sync(); | 84 | future.channel().closeFuture().sync(); |
| 73 | } catch (InterruptedException e) { | 85 | } catch (InterruptedException e) { |
| 74 | Thread.currentThread().interrupt(); | 86 | Thread.currentThread().interrupt(); |
| 75 | - log.error("HTTP 服务启动中断", e); | 87 | + log.warn("HTTP 服务启动被中断", e); |
| 76 | } finally { | 88 | } finally { |
| 77 | bossGroup.shutdownGracefully(); | 89 | bossGroup.shutdownGracefully(); |
| 78 | workerGroup.shutdownGracefully(); | 90 | workerGroup.shutdownGracefully(); |
| 79 | } | 91 | } |
| 80 | } | 92 | } |
| 81 | -} | 93 | + |
| 94 | +} | ||
| 82 | \ No newline at end of file | 95 | \ No newline at end of file |