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 | 66 | */ |
| 67 | 67 | private LocalDateTime returnTime; |
| 68 | 68 | /** |
| 69 | - * 回款方式,关联枚举{@link CrmReceivableReturnTypeEnum} | |
| 69 | + * 回款方式 | |
| 70 | + * | |
| 71 | + * 枚举 {@link CrmReceivableReturnTypeEnum} | |
| 70 | 72 | */ |
| 71 | 73 | private Integer returnType; |
| 72 | 74 | /** | ... | ... |
yudao-module-iot/yudao-module-iot-api/pom.xml
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 | 7 | * 服务注册表 - 插架模块使用,无法使用 Spring 注入 |
| 8 | 8 | */ |
| 9 | 9 | public class ServiceRegistry { |
| 10 | + | |
| 10 | 11 | private static final Map<Class<?>, Object> services = new HashMap<>(); |
| 11 | 12 | |
| 12 | 13 | /** |
| ... | ... | @@ -31,4 +32,5 @@ public class ServiceRegistry { |
| 31 | 32 | public static <T> T getService(Class<T> serviceClass) { |
| 32 | 33 | return (T) services.get(serviceClass); |
| 33 | 34 | } |
| 35 | + | |
| 34 | 36 | } |
| 35 | 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 | 14 | ErrorCode PRODUCT_KEY_EXISTS = new ErrorCode(1_050_001_001, "产品标识已经存在"); |
| 15 | 15 | ErrorCode PRODUCT_STATUS_NOT_DELETE = new ErrorCode(1_050_001_002, "产品状是发布状态,不允许删除"); |
| 16 | 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 | 18 | // ========== 产品物模型 1-050-002-000 ============ |
| 20 | 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 | 34 | } |
| 35 | 35 | |
| 36 | 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 | 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 | 34 | } |
| 35 | 35 | |
| 36 | 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 | 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 | 35 | return ARRAYS; |
| 36 | 36 | } |
| 37 | 37 | |
| 38 | - // TODO @haohao:可以使用 hutool 简化 | |
| 39 | 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 | 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 | 4 | import lombok.Getter; |
| 5 | 5 | |
| 6 | 6 | /** |
| 7 | - * IOT 访问方式枚举类 | |
| 7 | + * IOT 产品物模型属性读取类型枚举 | |
| 8 | 8 | * |
| 9 | 9 | * @author ahh |
| 10 | 10 | */ |
| 11 | 11 | @AllArgsConstructor |
| 12 | 12 | @Getter |
| 13 | -public enum IotProductThingModelAccessModeEnum { | |
| 13 | +public enum IotThingModelAccessModeEnum { | |
| 14 | 14 | |
| 15 | 15 | READ_ONLY("r"), |
| 16 | 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 | */ |
| 14 | 14 | @AllArgsConstructor |
| 15 | 15 | @Getter |
| 16 | -public enum IotProductThingModelTypeEnum implements IntArrayValuable { | |
| 16 | +public enum IotThingModelTypeEnum implements IntArrayValuable { | |
| 17 | 17 | |
| 18 | 18 | PROPERTY(1, "属性"), |
| 19 | 19 | SERVICE(2, "服务"), |
| 20 | 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 | 30 | */ |
| 31 | 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 | 35 | if (value.getType().equals(type)) { |
| 36 | 36 | return value; |
| 37 | 37 | } | ... | ... |
yudao-module-iot/yudao-module-iot-biz/pom.xml
| ... | ... | @@ -27,13 +27,6 @@ |
| 27 | 27 | |
| 28 | 28 | <dependency> |
| 29 | 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 | 30 | <artifactId>yudao-spring-boot-starter-biz-tenant</artifactId> |
| 38 | 31 | </dependency> |
| 39 | 32 | |
| ... | ... | @@ -81,6 +74,7 @@ |
| 81 | 74 | <dependency> |
| 82 | 75 | <groupId>org.pf4j</groupId> |
| 83 | 76 | <artifactId>pf4j-spring</artifactId> |
| 77 | + <!-- TODO @芋艿:可以放到 bom 里配置 --> | |
| 84 | 78 | <exclusions> |
| 85 | 79 | <exclusion> |
| 86 | 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 | 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 | 4 | import org.springframework.stereotype.Service; |
| 6 | 5 | import org.springframework.validation.annotation.Validated; |
| 7 | 6 | |
| ... | ... | @@ -15,11 +14,11 @@ import javax.annotation.Resource; |
| 15 | 14 | public class DeviceDataApiImpl implements DeviceDataApi { |
| 16 | 15 | |
| 17 | 16 | @Resource |
| 18 | - private IotDeviceDataService iotDeviceDataService; | |
| 17 | + private IotDevicePropertyDataService deviceDataService; | |
| 19 | 18 | |
| 20 | 19 | @Override |
| 21 | 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 | 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 | import cn.iocoder.yudao.module.iot.controller.admin.device.vo.deviceData.IotDeviceDataRespVO; |
| 8 | 8 | import cn.iocoder.yudao.module.iot.dal.dataobject.device.IotDeviceDataDO; |
| 9 | 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 | 11 | import io.swagger.v3.oas.annotations.Operation; |
| 12 | 12 | import io.swagger.v3.oas.annotations.tags.Tag; |
| 13 | 13 | import jakarta.annotation.Resource; |
| ... | ... | @@ -29,7 +29,7 @@ import static cn.iocoder.yudao.framework.common.pojo.CommonResult.success; |
| 29 | 29 | public class IotDeviceDataController { |
| 30 | 30 | |
| 31 | 31 | @Resource |
| 32 | - private IotDeviceDataService deviceDataService; | |
| 32 | + private IotDevicePropertyDataService deviceDataService; | |
| 33 | 33 | |
| 34 | 34 | // TODO @浩浩:这里的 /latest-list,包括方法名。 |
| 35 | 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 | 3 | import cn.iocoder.yudao.framework.common.pojo.CommonResult; |
| 4 | 4 | import cn.iocoder.yudao.framework.common.pojo.PageResult; |
| 5 | 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 | 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 | 12 | import io.swagger.v3.oas.annotations.Operation; |
| 12 | 13 | import io.swagger.v3.oas.annotations.Parameter; |
| 13 | 14 | import io.swagger.v3.oas.annotations.tags.Tag; |
| ... | ... | @@ -16,11 +17,8 @@ import jakarta.validation.Valid; |
| 16 | 17 | import org.springframework.security.access.prepost.PreAuthorize; |
| 17 | 18 | import org.springframework.validation.annotation.Validated; |
| 18 | 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 | 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 | 23 | @Tag(name = "管理后台 - IoT 插件信息") |
| 26 | 24 | @RestController |
| ... | ... | @@ -72,16 +70,11 @@ public class PluginInfoController { |
| 72 | 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 | 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 | 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 | 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 | 3 | import com.alibaba.excel.annotation.ExcelIgnoreUnannotated; |
| 4 | 4 | import com.alibaba.excel.annotation.ExcelProperty; |
| ... | ... | @@ -16,9 +16,9 @@ public class PluginInfoRespVO { |
| 16 | 16 | @ExcelProperty("主键 ID") |
| 17 | 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 | 23 | @Schema(description = "插件名称", requiredMode = Schema.RequiredMode.REQUIRED, example = "赵六") |
| 24 | 24 | @ExcelProperty("插件名称") |
| ... | ... | @@ -34,7 +34,7 @@ public class PluginInfoRespVO { |
| 34 | 34 | |
| 35 | 35 | @Schema(description = "插件包文件名", requiredMode = Schema.RequiredMode.REQUIRED) |
| 36 | 36 | @ExcelProperty("插件包文件名") |
| 37 | - private String file; | |
| 37 | + private String fileName; | |
| 38 | 38 | |
| 39 | 39 | @Schema(description = "插件版本", requiredMode = Schema.RequiredMode.REQUIRED) |
| 40 | 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 | 3 | import io.swagger.v3.oas.annotations.media.Schema; |
| 4 | 4 | import lombok.*; |
| ... | ... | @@ -10,8 +10,8 @@ public class PluginInfoSaveReqVO { |
| 10 | 10 | @Schema(description = "主键ID", requiredMode = Schema.RequiredMode.REQUIRED, example = "11546") |
| 11 | 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 | 16 | @Schema(description = "插件名称", requiredMode = Schema.RequiredMode.REQUIRED, example = "赵六") |
| 17 | 17 | private String name; |
| ... | ... | @@ -23,7 +23,7 @@ public class PluginInfoSaveReqVO { |
| 23 | 23 | private Integer deployType; |
| 24 | 24 | |
| 25 | 25 | @Schema(description = "插件包文件名", requiredMode = Schema.RequiredMode.REQUIRED) |
| 26 | - private String file; | |
| 26 | + private String fileName; | |
| 27 | 27 | |
| 28 | 28 | @Schema(description = "插件版本", requiredMode = Schema.RequiredMode.REQUIRED) |
| 29 | 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 | 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 | 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 | 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 | 25 | "defaultValue": "30", |
| 26 | 26 | "unit": "%", |
| 27 | 27 | "unitName": "百分比" |
| 28 | - }, | |
| 29 | - "description": "当前温度值" | |
| 28 | + } | |
| 30 | 29 | } |
| 31 | 30 | } |
| 32 | 31 | |
| ... | ... | @@ -46,7 +45,6 @@ Authorization: Bearer {{token}} |
| 46 | 45 | "property": { |
| 47 | 46 | "identifier": "switch", |
| 48 | 47 | "name": "开关", |
| 49 | - "description": "温度计开关", | |
| 50 | 48 | "accessMode": "rw", |
| 51 | 49 | "required": true, |
| 52 | 50 | "dataType": "bool", |
| ... | ... | @@ -81,7 +79,6 @@ Authorization: Bearer {{token}} |
| 81 | 79 | "property": { |
| 82 | 80 | "identifier": "argb", |
| 83 | 81 | "name": "温度计 argb 颜色", |
| 84 | - "description": "温度计 argb 颜色", | |
| 85 | 82 | "accessMode": "rw", |
| 86 | 83 | "required": true, |
| 87 | 84 | "dataType": "array", |
| ... | ... | @@ -93,7 +90,6 @@ Authorization: Bearer {{token}} |
| 93 | 90 | { |
| 94 | 91 | "identifier": "switch", |
| 95 | 92 | "name": "开关", |
| 96 | - "description": "温度计开关", | |
| 97 | 93 | "accessMode": "rw", |
| 98 | 94 | "required": true, |
| 99 | 95 | "dataType": "struct", |
| ... | ... | @@ -126,8 +122,7 @@ Authorization: Bearer {{token}} |
| 126 | 122 | "defaultValue": "30", |
| 127 | 123 | "unit": "%", |
| 128 | 124 | "unitName": "百分比" |
| 129 | - }, | |
| 130 | - "description": "当前温度值" | |
| 125 | + } | |
| 131 | 126 | } |
| 132 | 127 | ] |
| 133 | 128 | } |
| ... | ... | @@ -151,7 +146,6 @@ Authorization: Bearer {{token}} |
| 151 | 146 | "property": { |
| 152 | 147 | "identifier": "switch", |
| 153 | 148 | "name": "开关", |
| 154 | - "description": "温度计开关", | |
| 155 | 149 | "accessMode": "r", |
| 156 | 150 | "required": true, |
| 157 | 151 | "dataType": "bool", |
| ... | ... | @@ -176,7 +170,7 @@ tenant-id: {{adminTenentId}} |
| 176 | 170 | Authorization: Bearer {{token}} |
| 177 | 171 | |
| 178 | 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 | 174 | tenant-id: {{adminTenentId}} |
| 181 | 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 | 3 | import cn.iocoder.yudao.framework.common.pojo.CommonResult; |
| 4 | 4 | import cn.iocoder.yudao.framework.common.pojo.PageResult; |
| 5 | 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 | 12 | import io.swagger.v3.oas.annotations.Operation; |
| 13 | 13 | import io.swagger.v3.oas.annotations.Parameter; |
| 14 | 14 | import io.swagger.v3.oas.annotations.tags.Tag; |
| ... | ... | @@ -24,61 +24,61 @@ import static cn.iocoder.yudao.framework.common.pojo.CommonResult.success; |
| 24 | 24 | |
| 25 | 25 | @Tag(name = "管理后台 - IoT 产品物模型") |
| 26 | 26 | @RestController |
| 27 | -@RequestMapping("/iot/product-thing-model") | |
| 27 | +@RequestMapping("/iot/thing-model") | |
| 28 | 28 | @Validated |
| 29 | -public class IotProductThingModelController { | |
| 29 | +public class IotThingModelController { | |
| 30 | 30 | |
| 31 | 31 | @Resource |
| 32 | - private IotProductThingModelService thingModelService; | |
| 32 | + private IotThingModelService thingModelService; | |
| 33 | 33 | |
| 34 | 34 | @PostMapping("/create") |
| 35 | 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 | 41 | @PutMapping("/update") |
| 42 | 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 | 46 | return success(true); |
| 47 | 47 | } |
| 48 | 48 | |
| 49 | 49 | @DeleteMapping("/delete") |
| 50 | 50 | @Operation(summary = "删除产品物模型") |
| 51 | 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 | 55 | return success(true); |
| 56 | 56 | } |
| 57 | 57 | |
| 58 | 58 | @GetMapping("/get") |
| 59 | 59 | @Operation(summary = "获得产品物模型") |
| 60 | 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 | 67 | @GetMapping("/list-by-product-id") |
| 68 | 68 | @Operation(summary = "获得产品物模型") |
| 69 | 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 | 76 | @GetMapping("/page") |
| 77 | 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 | 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 | 4 | import lombok.Data; |
| 5 | + | |
| 5 | 6 | import java.util.List; |
| 6 | 7 | |
| 8 | +/** | |
| 9 | + * 物模型中的事件 | |
| 10 | + * | |
| 11 | + * @author HUIHUI | |
| 12 | + */ | |
| 7 | 13 | @Data |
| 8 | 14 | public class ThingModelEvent { |
| 9 | 15 | |
| ... | ... | @@ -16,17 +22,24 @@ public class ThingModelEvent { |
| 16 | 22 | */ |
| 17 | 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 | 33 | private String type; |
| 29 | - private List<ThingModelArgument> outputData; | |
| 34 | + /** | |
| 35 | + * 事件的输出参数 | |
| 36 | + * | |
| 37 | + * 输出参数定义事件调用后返回的结果或反馈信息,用于确认操作结果或提供额外的信息。 | |
| 38 | + */ | |
| 39 | + private List<ThingModelParam> outputParams; | |
| 40 | + /** | |
| 41 | + * 标识设备需要执行的具体操作 | |
| 42 | + */ | |
| 30 | 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 | 1 | package cn.iocoder.yudao.module.iot.controller.admin.thingmodel.model; |
| 2 | 2 | |
| 3 | 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 | 5 | import lombok.Data; |
| 6 | 6 | |
| 7 | 7 | import java.util.List; |
| ... | ... | @@ -25,12 +25,9 @@ public class ThingModelProperty { |
| 25 | 25 | */ |
| 26 | 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 | 32 | private String accessMode; |
| 36 | 33 | /** |
| ... | ... | @@ -42,6 +39,8 @@ public class ThingModelProperty { |
| 42 | 39 | private Boolean required; |
| 43 | 40 | /** |
| 44 | 41 | * 数据类型,与 dataSpecs 的 dataType 保持一致 |
| 42 | + * | |
| 43 | + * 枚举 {@link cn.iocoder.yudao.module.iot.enums.thingmodel.IotDataSpecsDataTypeEnum} | |
| 45 | 44 | */ |
| 46 | 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 | 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 | 4 | import lombok.Data; |
| 5 | + | |
| 5 | 6 | import java.util.List; |
| 6 | 7 | |
| 8 | +/** | |
| 9 | + * 物模型中的服务 | |
| 10 | + * | |
| 11 | + * @author HUIHUI | |
| 12 | + */ | |
| 7 | 13 | @Data |
| 8 | 14 | public class ThingModelService { |
| 9 | 15 | |
| ... | ... | @@ -16,18 +22,30 @@ public class ThingModelService { |
| 16 | 22 | */ |
| 17 | 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 | 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 | 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
yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/thingmodel/model/dataType/ThingModelStructDataSpecs.java
| 1 | 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 | 4 | import com.fasterxml.jackson.annotation.JsonIgnoreProperties; |
| 5 | 5 | import lombok.Data; |
| 6 | 6 | import lombok.EqualsAndHashCode; |
| ... | ... | @@ -26,19 +26,13 @@ public class ThingModelStructDataSpecs extends ThingModelDataSpecs { |
| 26 | 26 | */ |
| 27 | 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 | 33 | private String accessMode; |
| 37 | 34 | /** |
| 38 | - * 是否是标准品类的必选服务。 | |
| 39 | - * | |
| 40 | - * - true:是 | |
| 41 | - * - false:否 | |
| 35 | + * 是否是标准品类的必选服务 | |
| 42 | 36 | */ |
| 43 | 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 | 2 | |
| 3 | 3 | import cn.iocoder.yudao.framework.common.pojo.PageParam; |
| 4 | 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 | 6 | import io.swagger.v3.oas.annotations.media.Schema; |
| 7 | 7 | import jakarta.validation.constraints.NotNull; |
| 8 | 8 | import lombok.Data; |
| ... | ... | @@ -13,7 +13,7 @@ import lombok.ToString; |
| 13 | 13 | @Data |
| 14 | 14 | @EqualsAndHashCode(callSuper = true) |
| 15 | 15 | @ToString(callSuper = true) |
| 16 | -public class IotProductThingModelPageReqVO extends PageParam { | |
| 16 | +public class IotThingModelPageReqVO extends PageParam { | |
| 17 | 17 | |
| 18 | 18 | @Schema(description = "功能标识") |
| 19 | 19 | private String identifier; |
| ... | ... | @@ -22,7 +22,7 @@ public class IotProductThingModelPageReqVO extends PageParam { |
| 22 | 22 | private String name; |
| 23 | 23 | |
| 24 | 24 | @Schema(description = "功能类型", example = "1") |
| 25 | - @InEnum(IotProductThingModelTypeEnum.class) | |
| 25 | + @InEnum(IotThingModelTypeEnum.class) | |
| 26 | 26 | private Integer type; |
| 27 | 27 | |
| 28 | 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 | 13 | @Schema(description = "管理后台 - IoT 产品物模型 Response VO") |
| 14 | 14 | @Data |
| 15 | 15 | @ExcelIgnoreUnannotated |
| 16 | -public class IotProductThingModelRespVO { | |
| 16 | +public class IotThingModelRespVO { | |
| 17 | 17 | |
| 18 | 18 | @Schema(description = "产品ID", requiredMode = Schema.RequiredMode.REQUIRED, example = "21816") |
| 19 | 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 | 4 | import cn.iocoder.yudao.module.iot.controller.admin.thingmodel.model.ThingModelEvent; |
| 5 | 5 | import cn.iocoder.yudao.module.iot.controller.admin.thingmodel.model.ThingModelProperty; |
| 6 | 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 | 8 | import io.swagger.v3.oas.annotations.media.Schema; |
| 9 | 9 | import jakarta.validation.constraints.NotEmpty; |
| 10 | 10 | import jakarta.validation.constraints.NotNull; |
| ... | ... | @@ -12,7 +12,7 @@ import lombok.Data; |
| 12 | 12 | |
| 13 | 13 | @Schema(description = "管理后台 - IoT 产品物模型新增/修改 Request VO") |
| 14 | 14 | @Data |
| 15 | -public class IotProductThingModelSaveReqVO { | |
| 15 | +public class IotThingModelSaveReqVO { | |
| 16 | 16 | |
| 17 | 17 | @Schema(description = "编号", example = "1") |
| 18 | 18 | private Long id; |
| ... | ... | @@ -38,7 +38,7 @@ public class IotProductThingModelSaveReqVO { |
| 38 | 38 | |
| 39 | 39 | @Schema(description = "功能类型", requiredMode = Schema.RequiredMode.REQUIRED) |
| 40 | 40 | @NotNull(message = "功能类型不能为空") |
| 41 | - @InEnum(IotProductThingModelTypeEnum.class) | |
| 41 | + @InEnum(IotThingModelTypeEnum.class) | |
| 42 | 42 | private Integer type; |
| 43 | 43 | |
| 44 | 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 | 3 | import cn.iocoder.yudao.module.iot.controller.admin.thingmodel.model.ThingModelEvent; |
| 4 | 4 | import cn.iocoder.yudao.module.iot.controller.admin.thingmodel.model.ThingModelProperty; |
| 5 | 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 | 10 | import org.mapstruct.Mapper; |
| 11 | 11 | import org.mapstruct.Mapping; |
| 12 | 12 | import org.mapstruct.Named; |
| ... | ... | @@ -16,44 +16,44 @@ import java.util.List; |
| 16 | 16 | import java.util.Objects; |
| 17 | 17 | |
| 18 | 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 | 23 | // 将 SaveReqVO 转换为 DO |
| 24 | 24 | @Mapping(target = "property", expression = "java(convertToProperty(bean))") |
| 25 | 25 | @Mapping(target = "event", expression = "java(convertToEvent(bean))") |
| 26 | 26 | @Mapping(target = "service", expression = "java(convertToService(bean))") |
| 27 | - IotProductThingModelDO convert(IotProductThingModelSaveReqVO bean); | |
| 27 | + IotThingModelDO convert(IotThingModelSaveReqVO bean); | |
| 28 | 28 | |
| 29 | 29 | // 将 DO 转换为 RespVO |
| 30 | 30 | @Mapping(target = "property", source = "property") |
| 31 | 31 | @Mapping(target = "event", source = "event") |
| 32 | 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 | 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 | 41 | return bean.getProperty(); |
| 42 | 42 | } |
| 43 | 43 | return null; |
| 44 | 44 | } |
| 45 | 45 | |
| 46 | 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 | 49 | return bean.getEvent(); |
| 50 | 50 | } |
| 51 | 51 | return null; |
| 52 | 52 | } |
| 53 | 53 | |
| 54 | 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 | 57 | return bean.getService(); |
| 58 | 58 | } |
| 59 | 59 | return null; | ... | ... |
yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/dataobject/device/IotDeviceDataDO.java
| 1 | 1 | package cn.iocoder.yudao.module.iot.dal.dataobject.device; |
| 2 | 2 | |
| 3 | 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 | 5 | import lombok.AllArgsConstructor; |
| 6 | 6 | import lombok.Builder; |
| 7 | 7 | import lombok.Data; |
| ... | ... | @@ -30,7 +30,7 @@ public class IotDeviceDataDO { |
| 30 | 30 | /** |
| 31 | 31 | * 物模型编号 |
| 32 | 32 | * <p> |
| 33 | - * 关联 {@link IotProductThingModelDO#getId()} | |
| 33 | + * 关联 {@link IotThingModelDO#getId()} | |
| 34 | 34 | */ |
| 35 | 35 | private Long thingModelId; |
| 36 | 36 | |
| ... | ... | @@ -51,21 +51,21 @@ public class IotDeviceDataDO { |
| 51 | 51 | /** |
| 52 | 52 | * 属性标识符 |
| 53 | 53 | * <p> |
| 54 | - * 关联 {@link IotProductThingModelDO#getIdentifier()} | |
| 54 | + * 关联 {@link IotThingModelDO#getIdentifier()} | |
| 55 | 55 | */ |
| 56 | 56 | private String identifier; |
| 57 | 57 | |
| 58 | 58 | /** |
| 59 | 59 | * 属性名称 |
| 60 | 60 | * <p> |
| 61 | - * 关联 {@link IotProductThingModelDO#getName()} | |
| 61 | + * 关联 {@link IotThingModelDO#getName()} | |
| 62 | 62 | */ |
| 63 | 63 | private String name; |
| 64 | 64 | |
| 65 | 65 | /** |
| 66 | 66 | * 数据类型 |
| 67 | 67 | * <p> |
| 68 | - * 关联 {@link IotProductThingModelDO#getProperty()#getDataType()} | |
| 68 | + * 关联 {@link IotThingModelDO#getProperty()#getDataType()} | |
| 69 | 69 | */ |
| 70 | 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 | 1 | package cn.iocoder.yudao.module.iot.dal.dataobject.plugininfo; |
| 2 | 2 | |
| 3 | 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 | 7 | import com.baomidou.mybatisplus.annotation.KeySequence; |
| 5 | 8 | import com.baomidou.mybatisplus.annotation.TableId; |
| 6 | 9 | import com.baomidou.mybatisplus.annotation.TableName; |
| ... | ... | @@ -26,11 +29,10 @@ public class PluginInfoDO extends BaseDO { |
| 26 | 29 | */ |
| 27 | 30 | @TableId |
| 28 | 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 | 43 | private String description; |
| 42 | 44 | /** |
| 43 | 45 | * 部署方式 |
| 46 | + * <p> | |
| 47 | + * 枚举 {@link IotPluginDeployTypeEnum} | |
| 44 | 48 | */ |
| 45 | - // TODO @haohao:枚举 | |
| 46 | 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 | 57 | private String version; |
| 56 | 58 | /** |
| 57 | 59 | * 插件类型 |
| 60 | + * <p> | |
| 61 | + * 枚举 {@link IotPluginTypeEnum} | |
| 58 | 62 | */ |
| 59 | - // TODO @haohao:枚举 | |
| 60 | 63 | private Integer type; |
| 61 | 64 | /** |
| 62 | 65 | * 设备插件协议类型 |
| ... | ... | @@ -64,8 +67,9 @@ public class PluginInfoDO extends BaseDO { |
| 64 | 67 | private String protocol; |
| 65 | 68 | /** |
| 66 | 69 | * 状态 |
| 70 | + * <p> | |
| 71 | + * 枚举 {@link IotPluginStatusEnum} | |
| 67 | 72 | */ |
| 68 | - // TODO @haohao:枚举 | |
| 69 | 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 | 1 | package cn.iocoder.yudao.module.iot.dal.dataobject.plugininstance; |
| 2 | 2 | |
| 3 | 3 | import cn.iocoder.yudao.framework.mybatis.core.dataobject.BaseDO; |
| 4 | +import cn.iocoder.yudao.module.iot.dal.dataobject.plugininfo.PluginInfoDO; | |
| 4 | 5 | import com.baomidou.mybatisplus.annotation.KeySequence; |
| 5 | 6 | import com.baomidou.mybatisplus.annotation.TableId; |
| 6 | 7 | import com.baomidou.mybatisplus.annotation.TableName; |
| 7 | 8 | import lombok.*; |
| 8 | 9 | |
| 9 | -// TODO @haohao:一些必要的关联、枚举 | |
| 10 | + | |
| 10 | 11 | /** |
| 11 | 12 | * IoT 插件实例 DO |
| 12 | 13 | * |
| ... | ... | @@ -33,6 +34,8 @@ public class PluginInstanceDO extends BaseDO { |
| 33 | 34 | private String mainId; |
| 34 | 35 | /** |
| 35 | 36 | * 插件id |
| 37 | + * <p> | |
| 38 | + * 关联 {@link PluginInfoDO#getId()} | |
| 36 | 39 | */ |
| 37 | 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 | 5 | |
| 6 | 6 | import java.util.HashMap; |
| 7 | 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 | 12 | * FieldParser 类用于解析和转换物模型字段到 TDengine 字段 |
| ... | ... | @@ -15,6 +16,7 @@ public class FieldParser { |
| 15 | 16 | /** |
| 16 | 17 | * 物模型到td数据类型映射 |
| 17 | 18 | */ |
| 19 | + @Deprecated | |
| 18 | 20 | private static final HashMap<String, String> TYPE_MAPPING = new HashMap<>() { |
| 19 | 21 | { |
| 20 | 22 | put("INT", "INT"); |
| ... | ... | @@ -52,10 +54,7 @@ public class FieldParser { |
| 52 | 54 | * @return 字段列表 |
| 53 | 55 | */ |
| 54 | 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 | 64 | * @return 转换后的 TDengine 字段对象列表 |
| 66 | 65 | */ |
| 67 | 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 | 68 | String type = row.get(1).toString().toUpperCase(); |
| 71 | 69 | // TODO @puhui999:"NCHAR" 最好枚举下 |
| 72 | 70 | int dataLength = "NCHAR".equals(type) ? Integer.parseInt(row.get(2).toString()) : -1; |
| 73 | 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 | 79 | public static String getFieldDefine(TdFieldDO field) { |
| 81 | 80 | return "`" + field.getFieldName() + "`" + " " |
| 82 | 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 | 2 | |
| 3 | 3 | import lombok.Data; |
| 4 | 4 | |
| 5 | -import java.util.Set; | |
| 6 | - | |
| 7 | 5 | // TODO @haohao:类似这个,其实可以参考 mybatis plus,querywrapper,搞个 TdEngineQueryWrapper。这样看起来会更好懂。 |
| 8 | 6 | /** |
| 9 | 7 | * 查询DO |
| 10 | 8 | */ |
| 11 | 9 | @Data |
| 10 | +@Deprecated | |
| 12 | 11 | public class SelectDO { |
| 13 | 12 | |
| 14 | 13 | // TODO @haoha:database 是个单词 | ... | ... |
yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/dataobject/tdengine/SelectVisualDO.java
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 | 5 | import lombok.Data; |
| 6 | 6 | import lombok.NoArgsConstructor; |
| 7 | 7 | |
| 8 | +// TODO 芋艿:看看是不是后续简化掉。 | |
| 8 | 9 | /** |
| 9 | 10 | * TD 引擎的字段 |
| 10 | 11 | */ |
| ... | ... | @@ -12,6 +13,7 @@ import lombok.NoArgsConstructor; |
| 12 | 13 | @NoArgsConstructor |
| 13 | 14 | @AllArgsConstructor |
| 14 | 15 | @Builder |
| 16 | +@Deprecated | |
| 15 | 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
yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/dataobject/tdengine/TdRestApi.java
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 | 5 | import cn.iocoder.yudao.module.iot.controller.admin.thingmodel.model.ThingModelProperty; |
| 6 | 6 | import cn.iocoder.yudao.module.iot.controller.admin.thingmodel.model.ThingModelService; |
| 7 | 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 | 9 | import com.baomidou.mybatisplus.annotation.KeySequence; |
| 10 | 10 | import com.baomidou.mybatisplus.annotation.TableField; |
| 11 | 11 | import com.baomidou.mybatisplus.annotation.TableId; |
| ... | ... | @@ -19,17 +19,17 @@ import lombok.NoArgsConstructor; |
| 19 | 19 | /** |
| 20 | 20 | * IoT 产品物模型功能 DO |
| 21 | 21 | * <p> |
| 22 | - * 每个 {@link IotProductDO} 和 {@link IotProductThingModelDO} 是“一对多”的关系,它的每个属性、事件、服务都对应一条记录 | |
| 22 | + * 每个 {@link IotProductDO} 和 {@link IotThingModelDO} 是“一对多”的关系,它的每个属性、事件、服务都对应一条记录 | |
| 23 | 23 | * |
| 24 | 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 | 28 | @Data |
| 29 | 29 | @Builder |
| 30 | 30 | @NoArgsConstructor |
| 31 | 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 | 66 | /** |
| 67 | 67 | * 功能类型 |
| 68 | 68 | * <p> |
| 69 | - * 枚举 {@link IotProductThingModelTypeEnum} | |
| 69 | + * 枚举 {@link IotThingModelTypeEnum} | |
| 70 | 70 | */ |
| 71 | 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 | import cn.iocoder.yudao.framework.mybatis.core.query.LambdaQueryWrapperX; |
| 7 | 7 | import cn.iocoder.yudao.module.iot.controller.admin.device.vo.device.IotDevicePageReqVO; |
| 8 | 8 | import cn.iocoder.yudao.module.iot.dal.dataobject.device.IotDeviceDO; |
| 9 | +import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; | |
| 9 | 10 | import org.apache.ibatis.annotations.Mapper; |
| 10 | 11 | |
| 11 | 12 | import java.util.List; |
| ... | ... | @@ -47,7 +48,8 @@ public interface IotDeviceMapper extends BaseMapperX<IotDeviceDO> { |
| 47 | 48 | } |
| 48 | 49 | |
| 49 | 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 | 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 | 3 | import cn.iocoder.yudao.framework.common.pojo.PageResult; |
| 4 | 4 | import cn.iocoder.yudao.framework.mybatis.core.query.LambdaQueryWrapperX; |
| 5 | 5 | import cn.iocoder.yudao.framework.mybatis.core.mapper.BaseMapperX; |
| 6 | 6 | import cn.iocoder.yudao.module.iot.dal.dataobject.plugininfo.PluginInfoDO; |
| 7 | 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 | 11 | * IoT 插件信息 Mapper |
| ... | ... | @@ -17,19 +17,8 @@ public interface PluginInfoMapper extends BaseMapperX<PluginInfoDO> { |
| 17 | 17 | |
| 18 | 18 | default PageResult<PluginInfoDO> selectPage(PluginInfoPageReqVO reqVO) { |
| 19 | 19 | return selectPage(reqVO, new LambdaQueryWrapperX<PluginInfoDO>() |
| 20 | - .eqIfPresent(PluginInfoDO::getPluginId, reqVO.getPluginId()) | |
| 21 | 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 | 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 | 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 | 3 | import cn.iocoder.yudao.framework.common.pojo.PageResult; |
| 4 | 4 | import cn.iocoder.yudao.framework.mybatis.core.query.LambdaQueryWrapperX; |
| 5 | 5 | import cn.iocoder.yudao.framework.mybatis.core.mapper.BaseMapperX; |
| 6 | +import cn.iocoder.yudao.module.iot.controller.admin.plugin.vo.PluginInstancePageReqVO; | |
| 6 | 7 | import cn.iocoder.yudao.module.iot.dal.dataobject.plugininstance.PluginInstanceDO; |
| 7 | 8 | import org.apache.ibatis.annotations.Mapper; |
| 8 | -import cn.iocoder.yudao.module.iot.controller.admin.plugininstance.vo.*; | |
| 9 | 9 | |
| 10 | 10 | /** |
| 11 | 11 | * IoT 插件实例 Mapper |
| ... | ... | @@ -15,6 +15,12 @@ import cn.iocoder.yudao.module.iot.controller.admin.plugininstance.vo.*; |
| 15 | 15 | @Mapper |
| 16 | 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 | 24 | default PageResult<PluginInstanceDO> selectPage(PluginInstancePageReqVO reqVO) { |
| 19 | 25 | return selectPage(reqVO, new LambdaQueryWrapperX<PluginInstanceDO>() |
| 20 | 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 | 5 | import cn.iocoder.yudao.framework.mybatis.core.query.LambdaQueryWrapperX; |
| 6 | 6 | import cn.iocoder.yudao.module.iot.controller.admin.product.vo.product.IotProductPageReqVO; |
| 7 | 7 | import cn.iocoder.yudao.module.iot.dal.dataobject.product.IotProductDO; |
| 8 | +import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; | |
| 8 | 9 | import org.apache.ibatis.annotations.Mapper; |
| 9 | 10 | |
| 10 | 11 | /** |
| ... | ... | @@ -23,7 +24,8 @@ public interface IotProductMapper extends BaseMapperX<IotProductDO> { |
| 23 | 24 | } |
| 24 | 25 | |
| 25 | 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 | 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 | 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 | 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
yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/emq/service/EmqxServiceImpl.java
| 1 | 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 | 4 | import jakarta.annotation.Resource; |
| 6 | 5 | import lombok.extern.slf4j.Slf4j; |
| 7 | 6 | import org.eclipse.paho.client.mqttv3.MqttClient; |
| ... | ... | @@ -21,7 +20,7 @@ import org.springframework.stereotype.Service; |
| 21 | 20 | public class EmqxServiceImpl implements EmqxService { |
| 22 | 21 | |
| 23 | 22 | @Resource |
| 24 | - private IotDeviceDataService iotDeviceDataService; | |
| 23 | + private IotDevicePropertyDataService iotDeviceDataService; | |
| 25 | 24 | |
| 26 | 25 | // TODO 多线程处理消息 |
| 27 | 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 | 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 | 1 | package cn.iocoder.yudao.module.iot.framework.plugin; |
| 2 | 2 | |
| 3 | -import cn.iocoder.yudao.module.iot.api.DeviceDataApi; | |
| 4 | 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 | 6 | import lombok.extern.slf4j.Slf4j; |
| 7 | +import org.pf4j.spring.SpringPluginManager; | |
| 6 | 8 | import org.springframework.context.annotation.Bean; |
| 7 | 9 | import org.springframework.context.annotation.Configuration; |
| 10 | +import org.springframework.context.annotation.DependsOn; | |
| 8 | 11 | |
| 9 | -import javax.annotation.PostConstruct; | |
| 10 | 12 | import javax.annotation.Resource; |
| 11 | 13 | |
| 12 | 14 | @Slf4j |
| 13 | 15 | @Configuration |
| 14 | -public class ServiceRegistryConfiguration { | |
| 16 | +public class UnifiedConfiguration { | |
| 17 | + | |
| 18 | + private static final String SERVICE_REGISTRY_INITIALIZED_MARKER = "serviceRegistryInitializedMarker"; | |
| 15 | 19 | |
| 16 | 20 | @Resource |
| 17 | 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 | 25 | ServiceRegistry.registerService(DeviceDataApi.class, deviceDataApi); |
| 23 | 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 | 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 | 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 | 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 | 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
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 | 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 | 9 | import java.util.Map; |
| 10 | 10 | |
| 11 | 11 | /** |
| 12 | - * IoT 设备数据 Service 接口 | |
| 12 | + * IoT 设备【属性】数据 Service 接口 | |
| 13 | 13 | * |
| 14 | 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 | 28 | * @param productKey 产品 key |
| 22 | 29 | * @param deviceName 设备名称 |
| 23 | 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 | 33 | void saveDeviceData(String productKey, String deviceName, String message); |
| 27 | 34 | |
| ... | ... | @@ -40,4 +47,5 @@ public interface IotDeviceDataService { |
| 40 | 47 | * @return 设备属性历史数据 |
| 41 | 48 | */ |
| 42 | 49 | PageResult<Map<String, Object>> getHistoryDeviceProperties(@Valid IotDeviceDataPageReqVO deviceDataReqVO); |
| 50 | + | |
| 43 | 51 | } |
| 44 | 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 | 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 | 9 | import cn.iocoder.yudao.framework.common.util.object.BeanUtils; |
| 10 | 10 | import cn.iocoder.yudao.framework.common.util.validation.ValidationUtils; |
| 11 | 11 | import cn.iocoder.yudao.framework.tenant.core.aop.TenantIgnore; |
| 12 | +import cn.iocoder.yudao.framework.tenant.core.util.TenantUtils; | |
| 12 | 13 | import cn.iocoder.yudao.module.iot.controller.admin.device.vo.device.*; |
| 13 | 14 | import cn.iocoder.yudao.module.iot.dal.dataobject.device.IotDeviceDO; |
| 14 | 15 | import cn.iocoder.yudao.module.iot.dal.dataobject.device.IotDeviceGroupDO; |
| ... | ... | @@ -60,9 +61,11 @@ public class IotDeviceServiceImpl implements IotDeviceService { |
| 60 | 61 | throw exception(PRODUCT_NOT_EXISTS); |
| 61 | 62 | } |
| 62 | 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 | 69 | // 1.3 校验设备名称在同一产品下是否唯一 |
| 67 | 70 | if (deviceMapper.selectByProductKeyAndDeviceName(product.getProductKey(), createReqVO.getDeviceKey()) != null) { |
| 68 | 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 | 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 | 6 | import cn.iocoder.yudao.module.iot.dal.dataobject.plugininfo.PluginInfoDO; |
| 7 | 7 | import jakarta.validation.Valid; |
| 8 | 8 | import org.springframework.web.multipart.MultipartFile; |
| 9 | 9 | |
| 10 | +import java.util.List; | |
| 11 | + | |
| 10 | 12 | /** |
| 11 | 13 | * IoT 插件信息 Service 接口 |
| 12 | 14 | * |
| ... | ... | @@ -58,7 +60,7 @@ public interface PluginInfoService { |
| 58 | 60 | * @param id 插件id |
| 59 | 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 | 69 | * @param status 状态 |
| 68 | 70 | */ |
| 69 | 71 | void updatePluginStatus(Long id, Integer status); |
| 72 | + | |
| 73 | + /** | |
| 74 | + * 获得插件信息列表 | |
| 75 | + * | |
| 76 | + * @return 插件信息列表 | |
| 77 | + */ | |
| 78 | + List<PluginInfoDO> getPluginInfoList(); | |
| 70 | 79 | } |
| 71 | 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 | 3 | import cn.iocoder.yudao.framework.common.pojo.PageResult; |
| 5 | 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 | 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 | 9 | import cn.iocoder.yudao.module.iot.enums.plugin.IotPluginStatusEnum; |
| 12 | -import jakarta.annotation.PostConstruct; | |
| 13 | 10 | import jakarta.annotation.Resource; |
| 14 | -import lombok.SneakyThrows; | |
| 15 | 11 | import lombok.extern.slf4j.Slf4j; |
| 16 | 12 | import org.pf4j.PluginDescriptor; |
| 17 | 13 | import org.pf4j.PluginState; |
| ... | ... | @@ -22,11 +18,13 @@ import org.springframework.stereotype.Service; |
| 22 | 18 | import org.springframework.validation.annotation.Validated; |
| 23 | 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 | 26 | import java.util.concurrent.Executors; |
| 27 | 27 | import java.util.concurrent.TimeUnit; |
| 28 | -import java.util.jar.JarEntry; | |
| 29 | -import java.util.jar.JarFile; | |
| 30 | 28 | |
| 31 | 29 | import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception; |
| 32 | 30 | import static cn.iocoder.yudao.module.iot.enums.ErrorCodeConstants.*; |
| ... | ... | @@ -43,13 +41,9 @@ public class PluginInfoServiceImpl implements PluginInfoService { |
| 43 | 41 | |
| 44 | 42 | @Resource |
| 45 | 43 | private PluginInfoMapper pluginInfoMapper; |
| 46 | - | |
| 47 | 44 | @Resource |
| 48 | 45 | private SpringPluginManager pluginManager; |
| 49 | 46 | |
| 50 | - @Resource | |
| 51 | - private FileApi fileApi; | |
| 52 | - | |
| 53 | 47 | @Value("${pf4j.pluginsDir}") |
| 54 | 48 | private String pluginsDir; |
| 55 | 49 | |
| ... | ... | @@ -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 | 80 | if (plugin != null) { |
| 87 | 81 | // 查询插件是否是启动状态 |
| 88 | 82 | if (plugin.getPluginState().equals(PluginState.STARTED)) { |
| ... | ... | @@ -95,6 +89,19 @@ public class PluginInfoServiceImpl implements PluginInfoService { |
| 95 | 89 | |
| 96 | 90 | // 删除 |
| 97 | 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 | 107 | private PluginInfoDO validatePluginInfoExists(Long id) { |
| ... | ... | @@ -116,73 +123,100 @@ public class PluginInfoServiceImpl implements PluginInfoService { |
| 116 | 123 | } |
| 117 | 124 | |
| 118 | 125 | @Override |
| 119 | - public void uploadJar(Long id, MultipartFile file) { | |
| 120 | - // 1. 校验存在 | |
| 126 | + public void uploadFile(Long id, MultipartFile file) { | |
| 127 | + // 1. 校验插件信息是否存在 | |
| 121 | 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 | 149 | if (plugin != null) { |
| 130 | - // 3.2. 如果插件状态是启动的,停止插件 | |
| 131 | 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 | 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 | 172 | } catch (Exception e) { |
| 145 | 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 | 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 | 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 | 220 | pluginInfoDo.setVersion(pluginDescriptor.getVersion()); |
| 187 | 221 | pluginInfoDo.setDescription(pluginDescriptor.getPluginDescription()); |
| 188 | 222 | pluginInfoMapper.updateById(pluginInfoDo); |
| ... | ... | @@ -190,52 +224,47 @@ public class PluginInfoServiceImpl implements PluginInfoService { |
| 190 | 224 | |
| 191 | 225 | @Override |
| 192 | 226 | public void updatePluginStatus(Long id, Integer status) { |
| 193 | - // 1. 校验存在 | |
| 227 | + // 1. 校验插件信息是否存在 | |
| 194 | 228 | PluginInfoDO pluginInfoDo = validatePluginInfoExists(id); |
| 195 | 229 | |
| 196 | - // 插件状态无效 | |
| 230 | + // 2. 校验插件状态是否有效 | |
| 197 | 231 | if (!IotPluginStatusEnum.contains(status)) { |
| 198 | 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 | 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 | 253 | } else { |
| 212 | - // 已经停止,未获取到插件 | |
| 254 | + // 5. 插件不存在且状态为停止,抛出异常 | |
| 213 | 255 | if (IotPluginStatusEnum.STOPPED.getStatus().equals(pluginInfoDo.getStatus())) { |
| 214 | 256 | throw exception(PLUGIN_STATUS_INVALID); |
| 215 | 257 | } |
| 216 | 258 | } |
| 259 | + | |
| 260 | + // 6. 更新数据库中的插件状态 | |
| 217 | 261 | pluginInfoDo.setStatus(status); |
| 218 | 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 | 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
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 | 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 | 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 | 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 | 2 | |
| 3 | 3 | import cn.iocoder.yudao.framework.common.pojo.PageResult; |
| 4 | 4 | import cn.iocoder.yudao.framework.common.util.object.BeanUtils; |
| 5 | +import cn.iocoder.yudao.framework.tenant.core.util.TenantUtils; | |
| 5 | 6 | import cn.iocoder.yudao.module.iot.controller.admin.product.vo.product.IotProductPageReqVO; |
| 6 | 7 | import cn.iocoder.yudao.module.iot.controller.admin.product.vo.product.IotProductSaveReqVO; |
| 7 | 8 | import cn.iocoder.yudao.module.iot.dal.dataobject.product.IotProductDO; |
| 8 | 9 | import cn.iocoder.yudao.module.iot.dal.mysql.product.IotProductMapper; |
| 9 | 10 | import cn.iocoder.yudao.module.iot.enums.product.IotProductStatusEnum; |
| 11 | +import cn.iocoder.yudao.module.iot.service.device.IotDevicePropertyDataService; | |
| 10 | 12 | import cn.iocoder.yudao.module.iot.service.tdengine.IotThingModelMessageService; |
| 11 | -import cn.iocoder.yudao.module.iot.service.thingmodel.IotProductThingModelService; | |
| 12 | 13 | import com.baomidou.dynamic.datasource.annotation.DSTransactional; |
| 13 | 14 | import jakarta.annotation.Resource; |
| 14 | 15 | import org.springframework.context.annotation.Lazy; |
| ... | ... | @@ -34,18 +35,22 @@ public class IotProductServiceImpl implements IotProductService { |
| 34 | 35 | private IotProductMapper productMapper; |
| 35 | 36 | |
| 36 | 37 | @Resource |
| 37 | - @Lazy // 延迟加载,解决循环依赖 | |
| 38 | - private IotProductThingModelService thingModelFunctionService; | |
| 39 | - @Resource | |
| 40 | 38 | @Lazy // 延迟加载,解决循环依赖 |
| 41 | 39 | private IotThingModelMessageService thingModelMessageService; |
| 40 | + @Resource | |
| 41 | + @Lazy // 延迟加载,解决循环依赖 | |
| 42 | + private IotDevicePropertyDataService devicePropertyDataService; | |
| 42 | 43 | |
| 43 | 44 | @Override |
| 44 | 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 | 54 | // 2. 插入 |
| 50 | 55 | IotProductDO product = BeanUtils.toBean(createReqVO, IotProductDO.class) |
| 51 | 56 | .setStatus(IotProductStatusEnum.UNPUBLISHED.getStatus()); |
| ... | ... | @@ -119,8 +124,8 @@ public class IotProductServiceImpl implements IotProductService { |
| 119 | 124 | // 3. 产品是发布状态 |
| 120 | 125 | if (Objects.equals(status, IotProductStatusEnum.PUBLISHED.getStatus())) { |
| 121 | 126 | // 3.1 创建产品超级表数据模型 |
| 122 | - thingModelFunctionService.createSuperTableDataModel(id); | |
| 123 | - // 3.2 创建物模型日志超级表数据模型 | |
| 127 | + devicePropertyDataService.defineDevicePropertyData(id); | |
| 128 | + // 3.2 创建物模型日志超级表数据模型 TODO 待定:message 要不要分; | |
| 124 | 129 | thingModelMessageService.createSuperTable(id); |
| 125 | 130 | } |
| 126 | 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 | 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 | 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 | 11 | import cn.iocoder.yudao.module.iot.dal.dataobject.tdengine.TdFieldDO; |
| 12 | 12 | import cn.iocoder.yudao.module.iot.dal.dataobject.tdengine.TdTableDO; |
| 13 | 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 | 15 | import cn.iocoder.yudao.module.iot.dal.dataobject.product.IotProductDO; |
| 16 | 16 | import cn.iocoder.yudao.module.iot.dal.redis.deviceData.DeviceDataRedisDAO; |
| 17 | 17 | import cn.iocoder.yudao.module.iot.dal.tdengine.TdEngineDDLMapper; |
| 18 | 18 | import cn.iocoder.yudao.module.iot.dal.tdengine.TdEngineDMLMapper; |
| 19 | 19 | import cn.iocoder.yudao.module.iot.enums.IotConstants; |
| 20 | 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 | 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 | 24 | import cn.iocoder.yudao.module.iot.service.product.IotProductService; |
| 25 | 25 | import cn.iocoder.yudao.module.iot.util.IotTdDatabaseUtils; |
| 26 | 26 | import jakarta.annotation.Resource; |
| ... | ... | @@ -52,7 +52,7 @@ public class IotThingModelMessageServiceImpl implements IotThingModelMessageServ |
| 52 | 52 | private String url; |
| 53 | 53 | |
| 54 | 54 | @Resource |
| 55 | - private IotProductThingModelService iotProductThingModelService; | |
| 55 | + private IotThingModelService iotThingModelService; | |
| 56 | 56 | @Resource |
| 57 | 57 | private IotDeviceService iotDeviceService; |
| 58 | 58 | @Resource |
| ... | ... | @@ -83,7 +83,7 @@ public class IotThingModelMessageServiceImpl implements IotThingModelMessageServ |
| 83 | 83 | |
| 84 | 84 | // 2. 获取设备属性并进行物模型校验,过滤非物模型属性 |
| 85 | 85 | Map<String, Object> params = thingModelMessage.dataToMap(); |
| 86 | - List<IotProductThingModelDO> thingModelList = getValidThingModelList(thingModelMessage.getProductKey()); | |
| 86 | + List<IotThingModelDO> thingModelList = getValidThingModelList(thingModelMessage.getProductKey()); | |
| 87 | 87 | if (thingModelList.isEmpty()) { |
| 88 | 88 | return; |
| 89 | 89 | } |
| ... | ... | @@ -102,9 +102,9 @@ public class IotThingModelMessageServiceImpl implements IotThingModelMessageServ |
| 102 | 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 | 110 | @Override |
| ... | ... | @@ -134,21 +134,17 @@ public class IotThingModelMessageServiceImpl implements IotThingModelMessageServ |
| 134 | 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 | 143 | // 1. 获取属性标识符集合 |
| 148 | - Set<String> propertyIdentifiers = convertSet(thingModelList, IotProductThingModelDO::getIdentifier); | |
| 144 | + Set<String> propertyIdentifiers = convertSet(thingModelList, IotThingModelDO::getIdentifier); | |
| 149 | 145 | |
| 150 | 146 | // 2. 构建属性标识符和属性的映射 |
| 151 | - Map<String, IotProductThingModelDO> thingModelMap = convertMap(thingModelList, IotProductThingModelDO::getIdentifier); | |
| 147 | + Map<String, IotThingModelDO> thingModelMap = convertMap(thingModelList, IotThingModelDO::getIdentifier); | |
| 152 | 148 | |
| 153 | 149 | // 3. 过滤并收集有效的属性字段 |
| 154 | 150 | List<TdFieldDO> schemaFieldValues = new ArrayList<>(); |
| ... | ... | @@ -168,21 +164,21 @@ public class IotThingModelMessageServiceImpl implements IotThingModelMessageServ |
| 168 | 164 | * 缓存设备属性 |
| 169 | 165 | * |
| 170 | 166 | * @param device 设备信息 |
| 171 | - * @param iotProductThingModelDO 物模型属性 | |
| 167 | + * @param iotThingModelDO 物模型属性 | |
| 172 | 168 | * @param val 属性值 |
| 173 | 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 | 172 | IotDeviceDataDO deviceData = IotDeviceDataDO.builder() |
| 177 | 173 | .productKey(device.getProductKey()) |
| 178 | 174 | .deviceName(device.getDeviceName()) |
| 179 | - .identifier(iotProductThingModelDO.getIdentifier()) | |
| 175 | + .identifier(iotThingModelDO.getIdentifier()) | |
| 180 | 176 | .value(val != null ? val.toString() : null) |
| 181 | 177 | .updateTime(DateUtil.toLocalDateTime(new Date(time))) |
| 182 | 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 | 182 | .build(); |
| 187 | 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 | 1 | package cn.iocoder.yudao.module.iot.service.thingmodel; |
| 2 | 2 | |
| 3 | 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 | 7 | import jakarta.validation.Valid; |
| 8 | 8 | |
| 9 | 9 | import java.util.List; |
| ... | ... | @@ -13,7 +13,7 @@ import java.util.List; |
| 13 | 13 | * |
| 14 | 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 | * @param createReqVO 创建信息 |
| 22 | 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 | 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 | 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 | 43 | * @param id 编号 |
| 44 | 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 | 51 | * @param productId 产品编号 |
| 52 | 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 | 59 | * @param pageReqVO 分页查询 |
| 60 | 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 | 67 | * @param productKey 产品 Key |
| 75 | 68 | * @return 产品物模型列表 |
| 76 | 69 | */ |
| 77 | - List<IotProductThingModelDO> getProductThingModelListByProductKey(String productKey); | |
| 70 | + List<IotThingModelDO> getProductThingModelListByProductKey(String productKey); | |
| 78 | 71 | |
| 79 | 72 | } |
| 80 | 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 | 1 | package cn.iocoder.yudao.module.iot.util; |
| 2 | 2 | |
| 3 | +import cn.hutool.core.lang.Assert; | |
| 3 | 4 | import cn.hutool.core.util.StrUtil; |
| 4 | 5 | import cn.iocoder.yudao.module.iot.enums.IotConstants; |
| 5 | 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 | 8 | // TODO @芋艿:可能要思索下,有没更好的处理方式 |
| 13 | 9 | // TODO @芋艿:怎么改成无状态 |
| ... | ... | @@ -34,20 +30,17 @@ public class IotTdDatabaseUtils { |
| 34 | 30 | * @return 产品超级表表名 |
| 35 | 31 | */ |
| 36 | 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 | 34 | if (IotProductDeviceTypeEnum.GATEWAY_SUB.getType().equals(deviceType)) { |
| 42 | 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 | 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 | 51 | * |
| 59 | 52 | */ |
| 60 | 53 | public static String getThingModelMessageSuperTableName(String productKey) { |
| 61 | - // TODO @alwayssuper:是不是应该 + 拼接就好,不用 format | |
| 62 | 54 | return "thing_model_message_" + productKey.toLowerCase(); |
| 63 | 55 | } |
| 64 | 56 | |
| ... | ... | @@ -70,7 +62,8 @@ public class IotTdDatabaseUtils { |
| 70 | 62 | * @return 物模型日志设备表名 |
| 71 | 63 | */ |
| 72 | 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 | 46 | \ No newline at end of file | ... | ... |
yudao-module-iot/yudao-module-iot-biz/src/main/resources/mapper/plugininstance/PluginInstanceMapper.xml
| 1 | 1 | <?xml version="1.0" encoding="UTF-8"?> |
| 2 | 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 | 6 | 一般情况下,尽可能使用 Mapper 进行 CRUD 增删改查即可。 | ... | ... |
yudao-module-iot/yudao-module-iot-biz/src/main/resources/mapper/tdengine/TdThinkModelMessageMapper.xml
| ... | ... | @@ -5,7 +5,6 @@ |
| 5 | 5 | <mapper namespace="cn.iocoder.yudao.module.iot.dal.tdengine.TdThingModelMessageMapper"> |
| 6 | 6 | |
| 7 | 7 | <!-- 创建物模型消息日志超级表 --> |
| 8 | - <!-- TODO @芋艿:捉摸下字段,特别是 sys、ts 这种缩写 --> | |
| 9 | 8 | <update id="createSuperTable"> |
| 10 | 9 | CREATE STABLE ${dataBaseName}.${superTableName}( |
| 11 | 10 | ts TIMESTAMP, | ... | ... |
yudao-module-iot/yudao-module-iot-plugin/yudao-module-iot-http-plugin/pom.xml
| ... | ... | @@ -24,6 +24,7 @@ |
| 24 | 24 | <plugin.class>cn.iocoder.yudao.module.iot.plugin.HttpPlugin</plugin.class> |
| 25 | 25 | <plugin.version>0.0.1</plugin.version> |
| 26 | 26 | <plugin.provider>ahh</plugin.provider> |
| 27 | + <plugin.description>http-plugin-0.0.1</plugin.description> | |
| 27 | 28 | <plugin.dependencies/> |
| 28 | 29 | </properties> |
| 29 | 30 | |
| ... | ... | @@ -104,6 +105,7 @@ |
| 104 | 105 | <Plugin-Class>${plugin.class}</Plugin-Class> |
| 105 | 106 | <Plugin-Version>${plugin.version}</Plugin-Version> |
| 106 | 107 | <Plugin-Provider>${plugin.provider}</Plugin-Provider> |
| 108 | + <Plugin-Description>${plugin.description}</Plugin-Description> | |
| 107 | 109 | <Plugin-Dependencies>${plugin.dependencies}</Plugin-Dependencies> |
| 108 | 110 | </manifestEntries> |
| 109 | 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 | 2 | |
| 3 | 3 | import cn.hutool.json.JSONObject; |
| 4 | 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 | 6 | import io.netty.buffer.Unpooled; |
| 7 | 7 | import io.netty.channel.ChannelFutureListener; |
| 8 | 8 | import io.netty.channel.ChannelHandlerContext; |
| ... | ... | @@ -12,12 +12,9 @@ import io.netty.util.CharsetUtil; |
| 12 | 12 | |
| 13 | 13 | /** |
| 14 | 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 | 19 | public class HttpHandler extends SimpleChannelInboundHandler<FullHttpRequest> { |
| 23 | 20 | |
| ... | ... | @@ -29,10 +26,9 @@ public class HttpHandler extends SimpleChannelInboundHandler<FullHttpRequest> { |
| 29 | 26 | |
| 30 | 27 | @Override |
| 31 | 28 | protected void channelRead0(ChannelHandlerContext ctx, FullHttpRequest request) { |
| 32 | - String uri = request.uri(); | |
| 33 | - | |
| 34 | 29 | // 期望的路径格式: /sys/{productKey}/{deviceName}/thing/event/property/post |
| 35 | 30 | // 使用 "/" 拆分路径 |
| 31 | + String uri = request.uri(); | |
| 36 | 32 | String[] parts = uri.split("/"); |
| 37 | 33 | |
| 38 | 34 | /* |
| ... | ... | @@ -52,25 +48,19 @@ public class HttpHandler extends SimpleChannelInboundHandler<FullHttpRequest> { |
| 52 | 48 | && "event".equals(parts[5]) |
| 53 | 49 | && "property".equals(parts[6]) |
| 54 | 50 | && "post".equals(parts[7]); |
| 55 | - | |
| 56 | 51 | if (!isCorrectPath) { |
| 57 | - // 如果路径不匹配,返回 404 错误 | |
| 58 | 52 | writeResponse(ctx, HttpResponseStatus.NOT_FOUND, "Not Found"); |
| 59 | 53 | return; |
| 60 | 54 | } |
| 61 | - | |
| 62 | 55 | String productKey = parts[2]; |
| 63 | 56 | String deviceName = parts[3]; |
| 64 | 57 | |
| 65 | - // 从请求中获取原始数据 | |
| 58 | + // 从请求中获取原始数据,尝试解析请求数据为 JSON 对象 | |
| 66 | 59 | String requestBody = request.content().toString(CharsetUtil.UTF_8); |
| 67 | - | |
| 68 | - // 尝试解析请求数据为 JSON 对象 | |
| 69 | 60 | JSONObject jsonData; |
| 70 | 61 | try { |
| 71 | 62 | jsonData = JSONUtil.parseObj(requestBody); |
| 72 | 63 | } catch (Exception e) { |
| 73 | - // 数据不是合法的 JSON 格式,返回 400 错误 | |
| 74 | 64 | JSONObject res = createResponseJson( |
| 75 | 65 | 400, |
| 76 | 66 | new JSONObject(), |
| ... | ... | @@ -82,8 +72,6 @@ public class HttpHandler extends SimpleChannelInboundHandler<FullHttpRequest> { |
| 82 | 72 | writeResponse(ctx, HttpResponseStatus.BAD_REQUEST, res.toString()); |
| 83 | 73 | return; |
| 84 | 74 | } |
| 85 | - | |
| 86 | - // 获取请求中的 id 字段,若不存在则为 null | |
| 87 | 75 | String id = jsonData.getStr("id", null); |
| 88 | 76 | |
| 89 | 77 | try { |
| ... | ... | @@ -101,7 +89,6 @@ public class HttpHandler extends SimpleChannelInboundHandler<FullHttpRequest> { |
| 101 | 89 | ); |
| 102 | 90 | writeResponse(ctx, HttpResponseStatus.OK, successRes.toString()); |
| 103 | 91 | } catch (Exception e) { |
| 104 | - // 保存数据过程中出现异常,返回 500 错误 | |
| 105 | 92 | JSONObject errorRes = createResponseJson( |
| 106 | 93 | 500, |
| 107 | 94 | new JSONObject(), |
| ... | ... | @@ -128,7 +115,7 @@ public class HttpHandler extends SimpleChannelInboundHandler<FullHttpRequest> { |
| 128 | 115 | private JSONObject createResponseJson(int code, JSONObject data, String id, String message, String method, String version) { |
| 129 | 116 | JSONObject res = new JSONObject(); |
| 130 | 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 | 119 | res.set("id", id); |
| 133 | 120 | res.set("message", message); |
| 134 | 121 | res.set("method", method); |
| ... | ... | @@ -137,24 +124,24 @@ public class HttpHandler extends SimpleChannelInboundHandler<FullHttpRequest> { |
| 137 | 124 | } |
| 138 | 125 | |
| 139 | 126 | /** |
| 140 | - * 向客户端返回 HTTP 响应的辅助方法。 | |
| 127 | + * 向客户端返回 HTTP 响应的辅助方法 | |
| 141 | 128 | * |
| 142 | 129 | * @param ctx 通道上下文 |
| 143 | 130 | * @param status HTTP 响应状态码(网络层面的) |
| 144 | 131 | * @param content 响应内容(JSON 字符串或其他文本) |
| 145 | 132 | */ |
| 146 | 133 | private void writeResponse(ChannelHandlerContext ctx, HttpResponseStatus status, String content) { |
| 134 | + // 设置响应头为 JSON 类型和正确的编码 | |
| 147 | 135 | FullHttpResponse response = new DefaultFullHttpResponse( |
| 148 | 136 | HttpVersion.HTTP_1_1, |
| 149 | 137 | status, |
| 150 | 138 | Unpooled.copiedBuffer(content, CharsetUtil.UTF_8) |
| 151 | 139 | ); |
| 152 | - | |
| 153 | - // 设置响应头为 JSON 类型和正确的编码 | |
| 154 | 140 | response.headers().set(HttpHeaderNames.CONTENT_TYPE, "application/json; charset=UTF-8"); |
| 155 | 141 | response.headers().set(HttpHeaderNames.CONTENT_LENGTH, response.content().readableBytes()); |
| 156 | 142 | |
| 157 | 143 | // 发送响应并在发送完成后关闭连接 |
| 158 | 144 | ctx.writeAndFlush(response).addListener(ChannelFutureListener.CLOSE); |
| 159 | 145 | } |
| 146 | + | |
| 160 | 147 | } |
| 161 | 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 | 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 | 4 | import cn.iocoder.yudao.module.iot.api.ServiceRegistry; |
| 5 | 5 | import io.netty.bootstrap.ServerBootstrap; |
| 6 | 6 | import io.netty.channel.*; |
| ... | ... | @@ -18,18 +18,25 @@ import java.util.concurrent.Executors; |
| 18 | 18 | public class HttpPlugin extends Plugin { |
| 19 | 19 | |
| 20 | 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 | 25 | public HttpPlugin(PluginWrapper wrapper) { |
| 25 | 26 | super(wrapper); |
| 26 | - this.executorService = Executors.newSingleThreadExecutor(); // 创建单线程池 | |
| 27 | + // 初始化线程池 | |
| 28 | + this.executorService = Executors.newSingleThreadExecutor(); | |
| 27 | 29 | } |
| 28 | 30 | |
| 29 | 31 | @Override |
| 30 | 32 | public void start() { |
| 31 | 33 | log.info("HttpPlugin.start()"); |
| 32 | 34 | |
| 35 | + // 重新初始化线程池,确保它是活跃的 | |
| 36 | + if (executorService.isShutdown() || executorService.isTerminated()) { | |
| 37 | + executorService = Executors.newSingleThreadExecutor(); | |
| 38 | + } | |
| 39 | + | |
| 33 | 40 | // 从 ServiceRegistry 中获取主程序暴露的 DeviceDataApi 接口实例 |
| 34 | 41 | deviceDataApi = ServiceRegistry.getService(DeviceDataApi.class); |
| 35 | 42 | if (deviceDataApi == null) { |
| ... | ... | @@ -44,10 +51,13 @@ public class HttpPlugin extends Plugin { |
| 44 | 51 | @Override |
| 45 | 52 | public void stop() { |
| 46 | 53 | log.info("HttpPlugin.stop()"); |
| 47 | - executorService.shutdownNow(); // 停止线程池 | |
| 54 | + // 停止线程池 | |
| 55 | + executorService.shutdownNow(); | |
| 48 | 56 | } |
| 49 | 57 | |
| 50 | - // 启动 HTTP 服务 | |
| 58 | + /** | |
| 59 | + * 启动 HTTP 服务 | |
| 60 | + */ | |
| 51 | 61 | private void startHttpServer() { |
| 52 | 62 | EventLoopGroup bossGroup = new NioEventLoopGroup(1); |
| 53 | 63 | EventLoopGroup workerGroup = new NioEventLoopGroup(); |
| ... | ... | @@ -56,7 +66,8 @@ public class HttpPlugin extends Plugin { |
| 56 | 66 | ServerBootstrap bootstrap = new ServerBootstrap(); |
| 57 | 67 | bootstrap.group(bossGroup, workerGroup) |
| 58 | 68 | .channel(NioServerSocketChannel.class) |
| 59 | - .childHandler(new ChannelInitializer<Channel>() { | |
| 69 | + .childHandler(new ChannelInitializer<>() { | |
| 70 | + | |
| 60 | 71 | @Override |
| 61 | 72 | protected void initChannel(Channel channel) { |
| 62 | 73 | channel.pipeline().addLast(new HttpServerCodec()); |
| ... | ... | @@ -64,6 +75,7 @@ public class HttpPlugin extends Plugin { |
| 64 | 75 | // 将从 ServiceRegistry 获取的 deviceDataApi 传入处理器 |
| 65 | 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 | 84 | future.channel().closeFuture().sync(); |
| 73 | 85 | } catch (InterruptedException e) { |
| 74 | 86 | Thread.currentThread().interrupt(); |
| 75 | - log.error("HTTP 服务启动中断", e); | |
| 87 | + log.warn("HTTP 服务启动被中断", e); | |
| 76 | 88 | } finally { |
| 77 | 89 | bossGroup.shutdownGracefully(); |
| 78 | 90 | workerGroup.shutdownGracefully(); |
| 79 | 91 | } |
| 80 | 92 | } |
| 81 | -} | |
| 93 | + | |
| 94 | +} | |
| 82 | 95 | \ No newline at end of file | ... | ... |