Commit 1ce9420a8d77a68595ce319ac80ef7fd5f96d2c0

Authored by alwayssuper
2 parents f68f0de0 24a660b5

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