ソースを参照

修改:IOT 根据属性列表,自动生成属性上报事件和属性设置、获取服务

安浩浩 7 ヶ月 前
コミット
1f8576f643
22 ファイル変更662 行追加171 行削除
  1. 225 40
      yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/thinkmodelfunction/IotThinkModelFunctionController.http
  2. 12 0
      yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/thinkmodelfunction/thingModel/ThingModelArgument.java
  3. 9 0
      yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/thinkmodelfunction/thingModel/ThingModelArraySpecs.java
  4. 11 0
      yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/thinkmodelfunction/thingModel/ThingModelArrayType.java
  5. 10 0
      yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/thinkmodelfunction/thingModel/ThingModelBoolType.java
  6. 22 0
      yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/thinkmodelfunction/thingModel/ThingModelDataType.java
  7. 10 0
      yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/thinkmodelfunction/thingModel/ThingModelDateType.java
  8. 18 0
      yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/thinkmodelfunction/thingModel/ThingModelDoubleType.java
  9. 11 0
      yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/thinkmodelfunction/thingModel/ThingModelEnumType.java
  10. 14 0
      yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/thinkmodelfunction/thingModel/ThingModelEvent.java
  11. 18 0
      yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/thinkmodelfunction/thingModel/ThingModelFloatType.java
  12. 18 0
      yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/thinkmodelfunction/thingModel/ThingModelIntType.java
  13. 13 0
      yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/thinkmodelfunction/thingModel/ThingModelProperty.java
  14. 15 0
      yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/thinkmodelfunction/thingModel/ThingModelService.java
  15. 11 0
      yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/thinkmodelfunction/thingModel/ThingModelStructField.java
  16. 13 0
      yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/thinkmodelfunction/thingModel/ThingModelStructType.java
  17. 15 0
      yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/thinkmodelfunction/thingModel/ThingModelTextType.java
  18. 0 90
      yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/thinkmodelfunction/vo/IotThingModelProperty.java
  19. 6 3
      yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/thinkmodelfunction/vo/IotThinkModelFunctionRespVO.java
  20. 11 6
      yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/thinkmodelfunction/vo/IotThinkModelFunctionSaveReqVO.java
  21. 33 23
      yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/convert/thinkmodelfunction/IotThinkModelFunctionConvert.java
  22. 167 9
      yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/thinkmodelfunction/IotThinkModelFunctionServiceImpl.java

+ 225 - 40
yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/thinkmodelfunction/IotThinkModelFunctionController.http

@@ -5,46 +5,140 @@ tenant-id: {{adminTenentId}}
 Authorization: Bearer {{token}}
 
 {
-  "productId": 1,
-  "productKey": "123456",
+  "productId": 1002,
+  "productKey": "smart-sensor-002",
   "properties": [
     {
-      "identifier": "CurrentTemperature",
-      "name": "当前温度",
+      "identifier": "Temperature",
+      "name": "温度",
       "accessMode": "r",
       "required": true,
       "dataType": {
         "type": "float",
         "specs": {
-          "min": "-40",
-          "max": "120",
-          "unit": "°C",
-          "unitName": "摄氏度",
-          "step": "0.1"
+          "min": -40.0,
+          "max": 125.0,
+          "step": 0.1,
+          "unit": "℃"
         }
-      }
+      },
+      "description": "当前温度值"
     },
     {
-      "identifier": "CurrentHumidity",
-      "name": "当前湿度",
+      "identifier": "Humidity",
+      "name": "湿度",
       "accessMode": "r",
       "required": true,
       "dataType": {
         "type": "float",
         "specs": {
-          "min": "0",
-          "max": "100",
-          "unit": "%",
-          "unitName": "百分比",
-          "step": "0.1"
+          "min": 0.0,
+          "max": 100.0,
+          "step": 0.1,
+          "unit": "%"
         }
-      }
+      },
+      "description": "当前湿度值"
+    },
+    {
+      "identifier": "GeoLocation",
+      "name": "地理位置",
+      "accessMode": "r",
+      "required": false,
+      "dataType": {
+        "type": "struct",
+        "specs": [
+          {
+            "identifier": "Longitude",
+            "name": "经度",
+            "dataType": {
+              "type": "double",
+              "specs": {
+                "min": -180.0,
+                "max": 180.0,
+                "step": 0.000001,
+                "unit": "°"
+              }
+            },
+            "description": "设备所在位置的经度"
+          },
+          {
+            "identifier": "Latitude",
+            "name": "纬度",
+            "dataType": {
+              "type": "double",
+              "specs": {
+                "min": -90.0,
+                "max": 90.0,
+                "step": 0.000001,
+                "unit": "°"
+              }
+            },
+            "description": "设备所在位置的纬度"
+          }
+        ]
+      },
+      "description": "设备的地理位置信息"
+    }
+  ],
+  "services": [
+    {
+      "identifier": "Reboot",
+      "name": "重启设备",
+      "callType": "async",
+      "inputData": [],
+      "description": "远程重启设备",
+      "method": "thing.service.reboot"
+    },
+    {
+      "identifier": "SetThreshold",
+      "name": "设置温度阈值",
+      "callType": "sync",
+      "inputData": [
+        {
+          "identifier": "Threshold",
+          "name": "阈值",
+          "dataType": {
+            "type": "float",
+            "specs": {
+              "min": -40.0,
+              "max": 125.0,
+              "step": 0.1,
+              "unit": "℃"
+            }
+          },
+          "description": "报警温度阈值"
+        }
+      ],
+      "description": "设置设备的温度报警阈值",
+      "method": "thing.service.setThreshold"
     }
   ],
-  "services": "{}",
-  "events": "{}"
+  "events": [
+    {
+      "identifier": "HighTemperatureAlert",
+      "name": "高温报警",
+      "type": "alert",
+      "outputData": [
+        {
+          "identifier": "CurrentTemperature",
+          "name": "当前温度",
+          "dataType": {
+            "type": "float",
+            "specs": {
+              "unit": "℃"
+            }
+          },
+          "description": "触发报警时的温度值"
+        }
+      ],
+      "description": "当温度超过阈值时触发高温报警事件",
+      "method": "thing.event.highTemperatureAlert"
+    }
+  ]
 }
 
+
 ### 请求 /iot/think-model-function/update 接口 => 成功
 PUT {{baseUrl}}/iot/think-model-function/update
 Content-Type: application/json
@@ -53,46 +147,137 @@ Authorization: Bearer {{token}}
 
 {
   "id": 1,
-  "productId": 1,
-  "productKey": "123456",
+  "productId": 1001,
+  "productKey": "smart-sensor-001",
   "properties": [
     {
-      "identifier": "CurrentTemperature",
-      "name": "当前温度",
+      "identifier": "Temperature",
+      "name": "温度",
       "accessMode": "r",
       "required": true,
       "dataType": {
         "type": "float",
         "specs": {
-          "min": "-40",
-          "max": "130",
-          "unit": "°C",
-          "unitName": "摄氏度",
-          "step": "0.1"
+          "min": -40.0,
+          "max": 125.0,
+          "step": 0.1,
+          "unit": "℃"
         }
-      }
+      },
+      "description": "当前温度值"
     },
     {
-      "identifier": "CurrentHumidity",
-      "name": "当前湿度",
+      "identifier": "Humidity",
+      "name": "湿度",
       "accessMode": "r",
       "required": true,
       "dataType": {
         "type": "float",
         "specs": {
-          "min": "0",
-          "max": "100",
-          "unit": "%",
-          "unitName": "百分比",
-          "step": "0.1"
+          "min": 0.0,
+          "max": 100.0,
+          "step": 0.1,
+          "unit": "%"
         }
-      }
+      },
+      "description": "当前湿度值"
+    },
+    {
+      "identifier": "GeoLocation",
+      "name": "地理位置",
+      "accessMode": "r",
+      "required": false,
+      "dataType": {
+        "type": "struct",
+        "specs": [
+          {
+            "identifier": "Longitude",
+            "name": "经度",
+            "dataType": {
+              "type": "double",
+              "specs": {
+                "min": -180.0,
+                "max": 180.0,
+                "step": 0.000001,
+                "unit": "°"
+              }
+            },
+            "description": "设备所在位置的经度"
+          },
+          {
+            "identifier": "Latitude",
+            "name": "纬度",
+            "dataType": {
+              "type": "double",
+              "specs": {
+                "min": -90.0,
+                "max": 90.0,
+                "step": 0.000001,
+                "unit": "°"
+              }
+            },
+            "description": "设备所在位置的纬度"
+          }
+        ]
+      },
+      "description": "设备的地理位置信息"
+    }
+  ],
+  "services": [
+    {
+      "identifier": "Reboot",
+      "name": "重启设备",
+      "callType": "async",
+      "inputData": [],
+      "description": "远程重启设备"
+    },
+    {
+      "identifier": "SetThreshold",
+      "name": "设置温度阈值",
+      "callType": "sync",
+      "inputData": [
+        {
+          "identifier": "Threshold",
+          "name": "阈值",
+          "dataType": {
+            "type": "float",
+            "specs": {
+              "min": -40.0,
+              "max": 125.0,
+              "step": 0.1,
+              "unit": "℃"
+            }
+          },
+          "description": "报警温度阈值"
+        }
+      ],
+      "description": "设置设备的温度报警阈值"
     }
   ],
-  "services": "{}",
-  "events": "{}"
+  "events": [
+    {
+      "identifier": "HighTemperatureAlert",
+      "name": "高温报警",
+      "type": "alert",
+      "outputData": [
+        {
+          "identifier": "CurrentTemperature",
+          "name": "当前温度",
+          "dataType": {
+            "type": "float",
+            "specs": {
+              "unit": "℃"
+            }
+          },
+          "description": "触发报警时的温度值"
+        }
+      ],
+      "description": "当温度超过阈值时触发高温报警事件"
+    }
+  ]
 }
 
+
 ### 请求 /iot/think-model-function/get-by-product-key 接口 => 成功
 GET {{baseUrl}}/iot/think-model-function/get-by-product-key?productKey=123456
 tenant-id: {{adminTenentId}}

+ 12 - 0
yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/thinkmodelfunction/thingModel/ThingModelArgument.java

@@ -0,0 +1,12 @@
+package cn.iocoder.yudao.module.iot.controller.admin.thinkmodelfunction.thingModel;
+
+import lombok.Data;
+
+@Data
+public class ThingModelArgument {
+    private String identifier;
+    private String name;
+    private ThingModelDataType dataType;
+    private String direction; // 用于区分输入或输出参数,"input" 或 "output"
+    private String description;
+}

+ 9 - 0
yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/thinkmodelfunction/thingModel/ThingModelArraySpecs.java

@@ -0,0 +1,9 @@
+package cn.iocoder.yudao.module.iot.controller.admin.thinkmodelfunction.thingModel;
+
+import lombok.Data;
+
+@Data
+public class ThingModelArraySpecs {
+    private int size; // 数组长度
+    private ThingModelDataType item; // 数组元素的类型
+}

+ 11 - 0
yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/thinkmodelfunction/thingModel/ThingModelArrayType.java

@@ -0,0 +1,11 @@
+package cn.iocoder.yudao.module.iot.controller.admin.thinkmodelfunction.thingModel;
+
+import lombok.Data;
+import lombok.EqualsAndHashCode;
+
+@Data
+@EqualsAndHashCode(callSuper = true)
+public class ThingModelArrayType extends ThingModelDataType {
+    private ThingModelArraySpecs specs;
+}
+

+ 10 - 0
yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/thinkmodelfunction/thingModel/ThingModelBoolType.java

@@ -0,0 +1,10 @@
+package cn.iocoder.yudao.module.iot.controller.admin.thinkmodelfunction.thingModel;
+
+import lombok.Data;
+import lombok.EqualsAndHashCode;
+
+@Data
+@EqualsAndHashCode(callSuper = true)
+public class ThingModelBoolType extends ThingModelDataType {
+    // Bool 类型一般不需要额外的 specs
+}

+ 22 - 0
yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/thinkmodelfunction/thingModel/ThingModelDataType.java

@@ -0,0 +1,22 @@
+package cn.iocoder.yudao.module.iot.controller.admin.thinkmodelfunction.thingModel;
+
+import com.fasterxml.jackson.annotation.JsonSubTypes;
+import com.fasterxml.jackson.annotation.JsonTypeInfo;
+import lombok.Data;
+
+@Data
+@JsonTypeInfo(use = JsonTypeInfo.Id.NAME, property = "type", visible = true)
+@JsonSubTypes({
+        @JsonSubTypes.Type(value = ThingModelIntType.class, name = "int"),
+        @JsonSubTypes.Type(value = ThingModelFloatType.class, name = "float"),
+        @JsonSubTypes.Type(value = ThingModelDoubleType.class, name = "double"),
+        @JsonSubTypes.Type(value = ThingModelTextType.class, name = "text"),
+        @JsonSubTypes.Type(value = ThingModelDateType.class, name = "date"),
+        @JsonSubTypes.Type(value = ThingModelBoolType.class, name = "bool"),
+        @JsonSubTypes.Type(value = ThingModelEnumType.class, name = "enum"),
+        @JsonSubTypes.Type(value = ThingModelStructType.class, name = "struct"),
+        @JsonSubTypes.Type(value = ThingModelArrayType.class, name = "array")
+})
+public abstract class ThingModelDataType {
+    private String type;
+}

+ 10 - 0
yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/thinkmodelfunction/thingModel/ThingModelDateType.java

@@ -0,0 +1,10 @@
+package cn.iocoder.yudao.module.iot.controller.admin.thinkmodelfunction.thingModel;
+
+import lombok.Data;
+import lombok.EqualsAndHashCode;
+
+@Data
+@EqualsAndHashCode(callSuper = true)
+public class ThingModelDateType extends ThingModelDataType {
+    // Date 类型一般不需要额外的 specs
+}

+ 18 - 0
yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/thinkmodelfunction/thingModel/ThingModelDoubleType.java

@@ -0,0 +1,18 @@
+package cn.iocoder.yudao.module.iot.controller.admin.thinkmodelfunction.thingModel;
+
+import lombok.Data;
+import lombok.EqualsAndHashCode;
+
+@Data
+@EqualsAndHashCode(callSuper = true)
+public class ThingModelDoubleType extends ThingModelDataType {
+    private ThingModelDoubleSpecs specs;
+}
+
+@Data
+class ThingModelDoubleSpecs {
+    private Double min;
+    private Double max;
+    private Double step;
+    private String unit;
+}

+ 11 - 0
yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/thinkmodelfunction/thingModel/ThingModelEnumType.java

@@ -0,0 +1,11 @@
+package cn.iocoder.yudao.module.iot.controller.admin.thinkmodelfunction.thingModel;
+
+import lombok.Data;
+import lombok.EqualsAndHashCode;
+import java.util.Map;
+
+@Data
+@EqualsAndHashCode(callSuper = true)
+public class ThingModelEnumType extends ThingModelDataType {
+    private Map<String, String> specs; // 枚举值和描述的键值对
+}

+ 14 - 0
yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/thinkmodelfunction/thingModel/ThingModelEvent.java

@@ -0,0 +1,14 @@
+package cn.iocoder.yudao.module.iot.controller.admin.thinkmodelfunction.thingModel;
+
+import lombok.Data;
+import java.util.List;
+
+@Data
+public class ThingModelEvent {
+    private String identifier;
+    private String name;
+    private String type; // "info"、"alert"、"error"
+    private List<ThingModelArgument> outputData;
+    private String description;
+    private String method;
+}

+ 18 - 0
yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/thinkmodelfunction/thingModel/ThingModelFloatType.java

@@ -0,0 +1,18 @@
+package cn.iocoder.yudao.module.iot.controller.admin.thinkmodelfunction.thingModel;
+
+import lombok.Data;
+import lombok.EqualsAndHashCode;
+
+@Data
+@EqualsAndHashCode(callSuper = true)
+public class ThingModelFloatType extends ThingModelDataType {
+    private ThingModelFloatSpecs specs;
+}
+
+@Data
+class ThingModelFloatSpecs {
+    private Float min;
+    private Float max;
+    private Float step;
+    private String unit;
+}

+ 18 - 0
yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/thinkmodelfunction/thingModel/ThingModelIntType.java

@@ -0,0 +1,18 @@
+package cn.iocoder.yudao.module.iot.controller.admin.thinkmodelfunction.thingModel;
+
+import lombok.Data;
+import lombok.EqualsAndHashCode;
+
+@Data
+@EqualsAndHashCode(callSuper = true)
+public class ThingModelIntType extends ThingModelDataType {
+    private ThingModelIntSpecs specs;
+}
+
+@Data
+class ThingModelIntSpecs {
+    private Integer min;
+    private Integer max;
+    private Integer step;
+    private String unit;
+}

+ 13 - 0
yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/thinkmodelfunction/thingModel/ThingModelProperty.java

@@ -0,0 +1,13 @@
+package cn.iocoder.yudao.module.iot.controller.admin.thinkmodelfunction.thingModel;
+
+import lombok.Data;
+
+@Data
+public class ThingModelProperty {
+    private String identifier;
+    private String name;
+    private String accessMode; // "rw"、"r"、"w"
+    private boolean required;
+    private ThingModelDataType dataType;
+    private String description;
+}

+ 15 - 0
yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/thinkmodelfunction/thingModel/ThingModelService.java

@@ -0,0 +1,15 @@
+package cn.iocoder.yudao.module.iot.controller.admin.thinkmodelfunction.thingModel;
+
+import lombok.Data;
+import java.util.List;
+
+@Data
+public class ThingModelService {
+    private String identifier;
+    private String name;
+    private String callType; // "sync"、"async"
+    private List<ThingModelArgument> inputData;
+    private List<ThingModelArgument> outputData;
+    private String description;
+    private String method;
+}

+ 11 - 0
yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/thinkmodelfunction/thingModel/ThingModelStructField.java

@@ -0,0 +1,11 @@
+package cn.iocoder.yudao.module.iot.controller.admin.thinkmodelfunction.thingModel;
+
+import lombok.Data;
+
+@Data
+public class ThingModelStructField {
+    private String identifier;
+    private String name;
+    private ThingModelDataType dataType;
+    private String description;
+}

+ 13 - 0
yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/thinkmodelfunction/thingModel/ThingModelStructType.java

@@ -0,0 +1,13 @@
+package cn.iocoder.yudao.module.iot.controller.admin.thinkmodelfunction.thingModel;
+
+import lombok.Data;
+import lombok.EqualsAndHashCode;
+import java.util.List;
+
+@Data
+@EqualsAndHashCode(callSuper = true)
+public class ThingModelStructType extends ThingModelDataType {
+    private List<ThingModelStructField> specs;
+}
+
+

+ 15 - 0
yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/thinkmodelfunction/thingModel/ThingModelTextType.java

@@ -0,0 +1,15 @@
+package cn.iocoder.yudao.module.iot.controller.admin.thinkmodelfunction.thingModel;
+
+import lombok.Data;
+import lombok.EqualsAndHashCode;
+
+@Data
+@EqualsAndHashCode(callSuper = true)
+public class ThingModelTextType extends ThingModelDataType {
+    private ThingModelTextSpecs specs;
+}
+
+@Data
+class ThingModelTextSpecs {
+    private Integer length; // 最大长度
+}

+ 0 - 90
yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/thinkmodelfunction/vo/IotThingModelProperty.java

@@ -1,90 +0,0 @@
-package cn.iocoder.yudao.module.iot.controller.admin.thinkmodelfunction.vo;
-
-import io.swagger.v3.oas.annotations.media.Schema;
-import lombok.Data;
-
-import java.util.List;
-
-@Schema(description = "管理后台 - IoT 产品物模型属性")
-@Data
-public class IotThingModelProperty {
-
-    @Schema(description = "属性标识符")
-    private String identifier;
-
-    @Schema(description = "属性名称")
-    private String name;
-
-    @Schema(description = "访问模式 (r/rw)")
-    private String accessMode;
-
-    @Schema(description = "是否必需")
-    private boolean required;
-
-    @Schema(description = "数据类型")
-    private DataType dataType;
-
-    @Schema(description = "数据类型")
-    @Data
-    public static class DataType {
-
-        @Schema(description = "数据类型(float, double, struct, enum等)")
-        private String type;
-
-        @Schema(description = "单一类型的规格(适用于float, double等)")
-        private Specs specs;
-
-        @Schema(description = "结构体字段(适用于struct类型)")
-        private List<StructField> structSpecs;
-
-        @Schema(description = "规格")
-        @Data
-        public static class Specs {
-
-            @Schema(description = "最小值")
-            private String min;
-
-            @Schema(description = "最大值")
-            private String max;
-
-            @Schema(description = "单位符号")
-            private String unit;
-
-            @Schema(description = "单位名称")
-            private String unitName;
-
-            @Schema(description = "步进值")
-            private String step;
-        }
-
-        @Schema(description = "结构体字段")
-        @Data
-        public static class StructField {
-
-            @Schema(description = "字段标识符")
-            private String identifier;
-
-            @Schema(description = "字段名称")
-            private String name;
-
-            @Schema(description = "字段的数据类型")
-            private DataType dataType;
-        }
-    }
-
-    @Schema(description = "枚举规格")
-    @Data
-    public static class EnumSpecs {
-
-        @Schema(description = "枚举值")
-        private int value;
-
-        @Schema(description = "枚举名称")
-        private String name;
-
-        public EnumSpecs(int value, String name) {
-            this.value = value;
-            this.name = name;
-        }
-    }
-}

+ 6 - 3
yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/thinkmodelfunction/vo/IotThinkModelFunctionRespVO.java

@@ -1,5 +1,8 @@
 package cn.iocoder.yudao.module.iot.controller.admin.thinkmodelfunction.vo;
 
+import cn.iocoder.yudao.module.iot.controller.admin.thinkmodelfunction.thingModel.ThingModelEvent;
+import cn.iocoder.yudao.module.iot.controller.admin.thinkmodelfunction.thingModel.ThingModelProperty;
+import cn.iocoder.yudao.module.iot.controller.admin.thinkmodelfunction.thingModel.ThingModelService;
 import com.alibaba.excel.annotation.ExcelIgnoreUnannotated;
 import com.alibaba.excel.annotation.ExcelProperty;
 import io.swagger.v3.oas.annotations.media.Schema;
@@ -23,15 +26,15 @@ public class IotThinkModelFunctionRespVO {
 
     @Schema(description = "属性列表", requiredMode = Schema.RequiredMode.REQUIRED)
     @ExcelProperty("属性列表")
-    private List<IotThingModelProperty> properties;
+    private List<ThingModelProperty> properties;
 
     @Schema(description = "服务列表")
     @ExcelProperty("服务列表")
-    private String services;
+    private List<ThingModelService> services;
 
     @Schema(description = "事件列表")
     @ExcelProperty("事件列表")
-    private String events;
+    private List<ThingModelEvent> events;
 
     @Schema(description = "创建时间", requiredMode = Schema.RequiredMode.REQUIRED)
     @ExcelProperty("创建时间")

+ 11 - 6
yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/thinkmodelfunction/vo/IotThinkModelFunctionSaveReqVO.java

@@ -1,9 +1,14 @@
 package cn.iocoder.yudao.module.iot.controller.admin.thinkmodelfunction.vo;
 
+import cn.iocoder.yudao.module.iot.controller.admin.thinkmodelfunction.thingModel.ThingModelEvent;
+import cn.iocoder.yudao.module.iot.controller.admin.thinkmodelfunction.thingModel.ThingModelProperty;
+import cn.iocoder.yudao.module.iot.controller.admin.thinkmodelfunction.thingModel.ThingModelService;
 import io.swagger.v3.oas.annotations.media.Schema;
-import lombok.*;
-import java.util.*;
-import jakarta.validation.constraints.*;
+import jakarta.validation.constraints.NotEmpty;
+import jakarta.validation.constraints.NotNull;
+import lombok.Data;
+
+import java.util.List;
 
 @Schema(description = "管理后台 - IoT 产品物模型新增/修改 Request VO")
 @Data
@@ -22,12 +27,12 @@ public class IotThinkModelFunctionSaveReqVO {
 
     @Schema(description = "属性列表", requiredMode = Schema.RequiredMode.REQUIRED)
     @NotEmpty(message = "属性列表不能为空")
-    private List<IotThingModelProperty> properties;
+    private List<ThingModelProperty> properties;
 
     @Schema(description = "服务列表")
-    private String services;
+    private List<ThingModelService> services;
 
     @Schema(description = "事件列表")
-    private String events;
+    private List<ThingModelEvent> events;
 
 }

+ 33 - 23
yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/convert/thinkmodelfunction/IotThinkModelFunctionConvert.java

@@ -1,16 +1,17 @@
 package cn.iocoder.yudao.module.iot.convert.thinkmodelfunction;
 
 import cn.hutool.json.JSONUtil;
-import cn.iocoder.yudao.module.iot.controller.admin.thinkmodelfunction.vo.IotThingModelProperty;
+import cn.iocoder.yudao.module.iot.controller.admin.thinkmodelfunction.thingModel.ThingModelEvent;
+import cn.iocoder.yudao.module.iot.controller.admin.thinkmodelfunction.thingModel.ThingModelProperty;
+import cn.iocoder.yudao.module.iot.controller.admin.thinkmodelfunction.thingModel.ThingModelService;
 import cn.iocoder.yudao.module.iot.controller.admin.thinkmodelfunction.vo.IotThinkModelFunctionRespVO;
 import cn.iocoder.yudao.module.iot.controller.admin.thinkmodelfunction.vo.IotThinkModelFunctionSaveReqVO;
 import cn.iocoder.yudao.module.iot.dal.dataobject.thinkmodelfunction.IotThinkModelFunctionDO;
-import org.mapstruct.AfterMapping;
 import org.mapstruct.Mapper;
 import org.mapstruct.Mapping;
-import org.mapstruct.MappingTarget;
 import org.mapstruct.factory.Mappers;
 
+import java.util.ArrayList;
 import java.util.List;
 
 @Mapper
@@ -18,34 +19,43 @@ public interface IotThinkModelFunctionConvert {
 
     IotThinkModelFunctionConvert INSTANCE = Mappers.getMapper(IotThinkModelFunctionConvert.class);
 
-    // 将 SaveReqVO 转换为 DO
-    @Mapping(target = "properties", ignore = true)
+    // 将 SaveReqVO 转换为 DO 对象,处理 properties, services, events 字段
+    @Mapping(target = "properties", expression = "java(convertPropertiesToJson(bean.getProperties()))")
+    @Mapping(target = "services", expression = "java(convertServicesToJson(bean.getServices()))")
+    @Mapping(target = "events", expression = "java(convertEventsToJson(bean.getEvents()))")
     IotThinkModelFunctionDO convert(IotThinkModelFunctionSaveReqVO bean);
 
-    // 将 DO 转换为 RespVO
-    @Mapping(target = "properties", ignore = true)
+    default String convertPropertiesToJson(List<ThingModelProperty> properties) {
+        return properties != null ? JSONUtil.toJsonStr(properties) : "[]";
+    }
+
+    default String convertServicesToJson(List<ThingModelService> services) {
+        return services != null ? JSONUtil.toJsonStr(services) : "[]";
+    }
+
+    default String convertEventsToJson(List<ThingModelEvent> events) {
+        return events != null ? JSONUtil.toJsonStr(events) : "[]";
+    }
+
+    // 将 DO 转换为 RespVO 对象,处理 properties, services, events 字段
+    @Mapping(target = "properties", expression = "java(convertJsonToProperties(bean.getProperties()))")
+    @Mapping(target = "services", expression = "java(convertJsonToServices(bean.getServices()))")
+    @Mapping(target = "events", expression = "java(convertJsonToEvents(bean.getEvents()))")
     IotThinkModelFunctionRespVO convert(IotThinkModelFunctionDO bean);
 
-    // 处理 properties 字段的转换,从 VO 到 DO
-    @AfterMapping
-    default void convertPropertiesToDO(IotThinkModelFunctionSaveReqVO source, @MappingTarget IotThinkModelFunctionDO target) {
-        target.setProperties(JSONUtil.toJsonStr(source.getProperties()));
+    default List<ThingModelProperty> convertJsonToProperties(String propertiesJson) {
+        return propertiesJson != null ? JSONUtil.toList(propertiesJson, ThingModelProperty.class) : new ArrayList<>();
     }
 
-    // 处理 properties 字段的转换,从 DO 到 VO
-    @AfterMapping
-    default void convertPropertiesToVO(IotThinkModelFunctionDO source, @MappingTarget IotThinkModelFunctionRespVO target) {
-        target.setProperties(JSONUtil.toList(source.getProperties(), IotThingModelProperty.class));
+    default List<ThingModelService> convertJsonToServices(String servicesJson) {
+        return servicesJson != null ? JSONUtil.toList(servicesJson, ThingModelService.class) : new ArrayList<>();
+    }
+
+    default List<ThingModelEvent> convertJsonToEvents(String eventsJson) {
+        return eventsJson != null ? JSONUtil.toList(eventsJson, ThingModelEvent.class) : new ArrayList<>();
     }
 
     // 批量转换 DO 列表到 RespVO 列表
     List<IotThinkModelFunctionRespVO> convertList(List<IotThinkModelFunctionDO> list);
-
-    // 批量转换处理 properties 字段
-    @AfterMapping
-    default void convertPropertiesListToVO(List<IotThinkModelFunctionDO> sourceList, @MappingTarget List<IotThinkModelFunctionRespVO> targetList) {
-        for (int i = 0; i < sourceList.size(); i++) {
-            convertPropertiesToVO(sourceList.get(i), targetList.get(i));
-        }
-    }
 }
+

+ 167 - 9
yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/thinkmodelfunction/IotThinkModelFunctionServiceImpl.java

@@ -1,6 +1,7 @@
 package cn.iocoder.yudao.module.iot.service.thinkmodelfunction;
 
-import cn.iocoder.yudao.framework.common.exception.ServiceException;
+import cn.hutool.json.JSONUtil;
+import cn.iocoder.yudao.module.iot.controller.admin.thinkmodelfunction.thingModel.*;
 import cn.iocoder.yudao.module.iot.controller.admin.thinkmodelfunction.vo.IotThinkModelFunctionSaveReqVO;
 import cn.iocoder.yudao.module.iot.convert.thinkmodelfunction.IotThinkModelFunctionConvert;
 import cn.iocoder.yudao.module.iot.dal.dataobject.thinkmodelfunction.IotThinkModelFunctionDO;
@@ -10,6 +11,10 @@ import lombok.extern.slf4j.Slf4j;
 import org.springframework.stereotype.Service;
 import org.springframework.validation.annotation.Validated;
 
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.List;
+
 import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception;
 import static cn.iocoder.yudao.module.iot.enums.ErrorCodeConstants.THINK_MODEL_FUNCTION_EXISTS_BY_PRODUCT_KEY;
 import static cn.iocoder.yudao.module.iot.enums.ErrorCodeConstants.THINK_MODEL_FUNCTION_NOT_EXISTS;
@@ -27,10 +32,13 @@ public class IotThinkModelFunctionServiceImpl implements IotThinkModelFunctionSe
         log.info("创建物模型,参数:{}", createReqVO);
         // 验证 ProductKey 对应的产品物模型是否已存在
         validateThinkModelFunctionNotExistsByProductKey(createReqVO.getProductKey());
-        // 插入
+        // 转换请求对象为数据对象
         IotThinkModelFunctionDO thinkModelFunction = IotThinkModelFunctionConvert.INSTANCE.convert(createReqVO);
+        // 自动生成属性上报事件和属性设置、获取服务
+        generateDefaultEventsAndServices(createReqVO, thinkModelFunction);
+        // 插入数据库
         thinkModelFunctionMapper.insert(thinkModelFunction);
-        // 返回
+        // 返回生成的 ID
         return thinkModelFunction.getId();
     }
 
@@ -43,9 +51,9 @@ public class IotThinkModelFunctionServiceImpl implements IotThinkModelFunctionSe
     @Override
     public void deleteThinkModelFunction(Long id) {
         log.info("删除物模型,id:{}", id);
-        // 校验存在
+        // 校验物模型是否存在
         validateThinkModelFunctionExists(id);
-        // 删除
+        // 删除物模型
         thinkModelFunctionMapper.deleteById(id);
     }
 
@@ -68,12 +76,15 @@ public class IotThinkModelFunctionServiceImpl implements IotThinkModelFunctionSe
     @Override
     public void updateThinkModelFunction(IotThinkModelFunctionSaveReqVO updateReqVO) {
         log.info("更新物模型,参数:{}", updateReqVO);
-        // 校验存在
+        // 校验物模型是否存在
         validateThinkModelFunctionExists(updateReqVO.getId());
-        // 校验 productKey 是否重复
+        // 校验 ProductKey 是否唯一
         validateProductKeyUnique(updateReqVO.getId(), updateReqVO.getProductKey());
-        // 更新
+        // 转换请求对象为数据对象
         IotThinkModelFunctionDO thinkModelFunction = IotThinkModelFunctionConvert.INSTANCE.convert(updateReqVO);
+        // 自动生成或更新属性上报事件和属性设置、获取服务
+        generateDefaultEventsAndServices(updateReqVO, thinkModelFunction);
+        // 更新数据库
         thinkModelFunctionMapper.updateById(thinkModelFunction);
     }
 
@@ -83,4 +94,151 @@ public class IotThinkModelFunctionServiceImpl implements IotThinkModelFunctionSe
             throw exception(THINK_MODEL_FUNCTION_EXISTS_BY_PRODUCT_KEY);
         }
     }
-}
+
+    /**
+     * @ TODO 还要再优化
+     * 根据属性列表,自动生成属性上报事件和属性设置、获取服务
+     */
+    private void generateDefaultEventsAndServices(IotThinkModelFunctionSaveReqVO reqVO, IotThinkModelFunctionDO thinkModelFunction) {
+        // 获取属性列表
+        List<ThingModelProperty> properties = reqVO.getProperties();
+        if (properties == null) {
+            properties = new ArrayList<>();
+        }
+
+        // 生成属性上报事件
+        List<ThingModelEvent> events = reqVO.getEvents() != null ? new ArrayList<>(reqVO.getEvents()) : new ArrayList<>();
+        ThingModelEvent propertyPostEvent = generatePropertyPostEvent(properties);
+        events.add(propertyPostEvent);
+
+        // 生成属性设置和获取服务
+        List<ThingModelService> services = reqVO.getServices() != null ? new ArrayList<>(reqVO.getServices()) : new ArrayList<>();
+        ThingModelService propertySetService = generatePropertySetService(properties);
+        if (propertySetService != null) {
+            services.add(propertySetService);
+        }
+        ThingModelService propertyGetService = generatePropertyGetService(properties);
+        if (propertyGetService != null) {
+            services.add(propertyGetService);
+        }
+
+        // 更新 thinkModelFunction 对象的 events 和 services 字段
+        thinkModelFunction.setEvents(JSONUtil.toJsonStr(events));
+        thinkModelFunction.setServices(JSONUtil.toJsonStr(services));
+    }
+
+    /**
+     * 生成属性上报事件
+     */
+    private ThingModelEvent generatePropertyPostEvent(List<ThingModelProperty> properties) {
+        ThingModelEvent event = new ThingModelEvent();
+        event.setIdentifier("post");
+        event.setName("属性上报");
+        event.setType("info");
+        event.setDescription("属性上报事件");
+        event.setMethod("thing.event.property.post");
+
+        // 将属性列表转换为事件的输出参数
+        List<ThingModelArgument> outputData = new ArrayList<>();
+        for (ThingModelProperty property : properties) {
+            ThingModelArgument arg = new ThingModelArgument();
+            arg.setIdentifier(property.getIdentifier());
+            arg.setName(property.getName());
+            arg.setDataType(property.getDataType());
+            arg.setDescription(property.getDescription());
+            arg.setDirection("output"); // 设置为输出参数
+            outputData.add(arg);
+        }
+        event.setOutputData(outputData);
+
+        return event;
+    }
+
+    /**
+     * 生成属性设置服务
+     */
+    private ThingModelService generatePropertySetService(List<ThingModelProperty> properties) {
+        List<ThingModelArgument> inputData = new ArrayList<>();
+        for (ThingModelProperty property : properties) {
+            if ("w".equals(property.getAccessMode()) || "rw".equals(property.getAccessMode())) {
+                ThingModelArgument arg = new ThingModelArgument();
+                arg.setIdentifier(property.getIdentifier());
+                arg.setName(property.getName());
+                arg.setDataType(property.getDataType());
+                arg.setDescription(property.getDescription());
+                arg.setDirection("input"); // 设置为输入参数
+                inputData.add(arg);
+            }
+        }
+        if (inputData.isEmpty()) {
+            // 如果没有可写属性,不生成属性设置服务
+            return null;
+        }
+
+        ThingModelService service = new ThingModelService();
+        service.setIdentifier("set");
+        service.setName("属性设置");
+        service.setCallType("async");
+        service.setDescription("属性设置服务");
+        service.setMethod("thing.service.property.set");
+        service.setInputData(inputData);
+        // 属性设置服务一般不需要输出参数
+        service.setOutputData(new ArrayList<>());
+
+        return service;
+    }
+
+    /**
+     * 生成属性获取服务
+     */
+    private ThingModelService generatePropertyGetService(List<ThingModelProperty> properties) {
+        List<ThingModelArgument> outputData = new ArrayList<>();
+        for (ThingModelProperty property : properties) {
+            if ("r".equals(property.getAccessMode()) || "rw".equals(property.getAccessMode())) {
+                ThingModelArgument arg = new ThingModelArgument();
+                arg.setIdentifier(property.getIdentifier());
+                arg.setName(property.getName());
+                arg.setDataType(property.getDataType());
+                arg.setDescription(property.getDescription());
+                arg.setDirection("output"); // 设置为输出参数
+                outputData.add(arg);
+            }
+        }
+        if (outputData.isEmpty()) {
+            // 如果没有可读属性,不生成属性获取服务
+            return null;
+        }
+
+        ThingModelService service = new ThingModelService();
+        service.setIdentifier("get");
+        service.setName("属性获取");
+        service.setCallType("async");
+        service.setDescription("属性获取服务");
+        service.setMethod("thing.service.property.get");
+
+        // 定义输入参数:属性标识符列表
+        ThingModelArgument inputArg = new ThingModelArgument();
+        inputArg.setIdentifier("properties");
+        inputArg.setName("属性标识符列表");
+        inputArg.setDescription("需要获取的属性标识符列表");
+        inputArg.setDirection("input"); // 设置为输入参数
+
+        // 创建数组类型,元素类型为文本类型(字符串)
+        ThingModelArrayType arrayType = new ThingModelArrayType();
+        arrayType.setType("array");
+        ThingModelArraySpecs arraySpecs = new ThingModelArraySpecs();
+        // 不指定数组长度,size 可以为 0 或者省略
+        ThingModelTextType textType = new ThingModelTextType();
+        textType.setType("text");
+        // 如果有需要,可以设置 TextType 的 specs,如长度限制
+        arraySpecs.setItem(textType);
+        arrayType.setSpecs(arraySpecs);
+
+        inputArg.setDataType(arrayType);
+
+        service.setInputData(Collections.singletonList(inputArg));
+        service.setOutputData(outputData);
+
+        return service;
+    }
+}