Эх сурвалжийг харах

【功能新增】IoT: 增加产品脚本管理功能,包括增删改查及测试接口

安浩浩 7 сар өмнө
parent
commit
e26903b06b
13 өөрчлөгдсөн 758 нэмэгдсэн , 1 устгасан
  1. 3 0
      yudao-module-iot/yudao-module-iot-api/src/main/java/cn/iocoder/yudao/module/iot/enums/ErrorCodeConstants.java
  2. 8 1
      yudao-module-iot/yudao-module-iot-biz/pom.xml
  3. 99 0
      yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/product/IotProductScriptController.java
  4. 46 0
      yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/product/vo/script/IotProductScriptPageReqVO.java
  5. 63 0
      yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/product/vo/script/IotProductScriptRespVO.java
  6. 42 0
      yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/product/vo/script/IotProductScriptSaveReqVO.java
  7. 35 0
      yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/product/vo/script/IotProductScriptTestReqVO.java
  8. 39 0
      yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/product/vo/script/IotProductScriptTestRespVO.java
  9. 19 0
      yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/product/vo/script/IotProductScriptUpdateStatusReqVO.java
  10. 72 0
      yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/dataobject/product/IotProductScriptDO.java
  11. 31 0
      yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/mysql/product/IotProductScriptMapper.java
  12. 82 0
      yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/product/IotProductScriptService.java
  13. 219 0
      yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/product/IotProductScriptServiceImpl.java

+ 3 - 0
yudao-module-iot/yudao-module-iot-api/src/main/java/cn/iocoder/yudao/module/iot/enums/ErrorCodeConstants.java

@@ -75,4 +75,7 @@ public interface ErrorCodeConstants {
     // ========== IoT 规则场景(场景联动) 1-050-011-000 ==========
     ErrorCode RULE_SCENE_NOT_EXISTS = new ErrorCode(1_050_011_000, "IoT 规则场景(场景联动)不存在");
 
+    // ========== IoT 产品脚本信息 1-050-012-000 ==========
+    ErrorCode PRODUCT_SCRIPT_NOT_EXISTS = new ErrorCode(1_050_012_000, "IoT 产品脚本信息不存在");
+
 }

+ 8 - 1
yudao-module-iot/yudao-module-iot-biz/pom.xml

@@ -69,6 +69,13 @@
             <artifactId>yudao-spring-boot-starter-excel</artifactId>
         </dependency>
 
+        <!-- 脚本插件相关 -->
+        <dependency>
+            <groupId>cn.iocoder.boot</groupId>
+            <artifactId>yudao-module-iot-plugin-script</artifactId>
+            <version>${revision}</version>
+        </dependency>
+
         <!-- 消息队列相关 -->
         <dependency>
             <groupId>org.apache.rocketmq</groupId>
@@ -87,7 +94,7 @@
         </dependency>
 
         <dependency>
-            <groupId>org.pf4j</groupId>  <!-- PF4J:内置插件机制 -->
+            <groupId>org.pf4j</groupId>            <!-- PF4J:内置插件机制 -->
             <artifactId>pf4j-spring</artifactId>
         </dependency>
 

+ 99 - 0
yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/product/IotProductScriptController.java

@@ -0,0 +1,99 @@
+package cn.iocoder.yudao.module.iot.controller.admin.product;
+
+import cn.iocoder.yudao.framework.common.pojo.CommonResult;
+import cn.iocoder.yudao.framework.common.pojo.PageResult;
+import cn.iocoder.yudao.framework.common.util.object.BeanUtils;
+import cn.iocoder.yudao.module.iot.controller.admin.product.vo.script.*;
+import cn.iocoder.yudao.module.iot.dal.dataobject.product.IotProductScriptDO;
+import cn.iocoder.yudao.module.iot.service.product.IotProductScriptService;
+import io.swagger.v3.oas.annotations.Operation;
+import io.swagger.v3.oas.annotations.Parameter;
+import io.swagger.v3.oas.annotations.tags.Tag;
+import jakarta.annotation.Resource;
+import jakarta.validation.Valid;
+import org.springframework.security.access.prepost.PreAuthorize;
+import org.springframework.validation.annotation.Validated;
+import org.springframework.web.bind.annotation.*;
+
+import java.util.List;
+
+import static cn.iocoder.yudao.framework.common.pojo.CommonResult.success;
+
+@Tag(name = "管理后台 - IoT 产品脚本信息")
+@RestController
+@RequestMapping("/iot/product-script")
+@Validated
+public class IotProductScriptController {
+
+    @Resource
+    private IotProductScriptService productScriptService;
+
+    @PostMapping("/create")
+    @Operation(summary = "创建产品脚本")
+    @PreAuthorize("@ss.hasPermission('iot:product-script:create')")
+    public CommonResult<Long> createProductScript(@Valid @RequestBody IotProductScriptSaveReqVO createReqVO) {
+        return success(productScriptService.createProductScript(createReqVO));
+    }
+
+    @PutMapping("/update")
+    @Operation(summary = "更新产品脚本")
+    @PreAuthorize("@ss.hasPermission('iot:product-script:update')")
+    public CommonResult<Boolean> updateProductScript(@Valid @RequestBody IotProductScriptSaveReqVO updateReqVO) {
+        productScriptService.updateProductScript(updateReqVO);
+        return success(true);
+    }
+
+    @DeleteMapping("/delete")
+    @Operation(summary = "删除产品脚本")
+    @Parameter(name = "id", description = "编号", required = true)
+    @PreAuthorize("@ss.hasPermission('iot:product-script:delete')")
+    public CommonResult<Boolean> deleteProductScript(@RequestParam("id") Long id) {
+        productScriptService.deleteProductScript(id);
+        return success(true);
+    }
+
+    @GetMapping("/get")
+    @Operation(summary = "获得产品脚本详情")
+    @Parameter(name = "id", description = "编号", required = true, example = "1024")
+    @PreAuthorize("@ss.hasPermission('iot:product-script:query')")
+    public CommonResult<IotProductScriptRespVO> getProductScript(@RequestParam("id") Long id) {
+        IotProductScriptDO productScript = productScriptService.getProductScript(id);
+        return success(BeanUtils.toBean(productScript, IotProductScriptRespVO.class));
+    }
+
+    @GetMapping("/list-by-product")
+    @Operation(summary = "获得产品的脚本列表")
+    @Parameter(name = "productId", description = "产品编号", required = true, example = "1024")
+    @PreAuthorize("@ss.hasPermission('iot:product-script:query')")
+    public CommonResult<List<IotProductScriptRespVO>> getProductScriptListByProductId(
+            @RequestParam("productId") Long productId) {
+        List<IotProductScriptDO> list = productScriptService.getProductScriptListByProductId(productId);
+        return success(BeanUtils.toBean(list, IotProductScriptRespVO.class));
+    }
+
+    @GetMapping("/page")
+    @Operation(summary = "获得产品脚本分页")
+    @PreAuthorize("@ss.hasPermission('iot:product-script:query')")
+    public CommonResult<PageResult<IotProductScriptRespVO>> getProductScriptPage(
+            @Valid IotProductScriptPageReqVO pageReqVO) {
+        PageResult<IotProductScriptDO> pageResult = productScriptService.getProductScriptPage(pageReqVO);
+        return success(BeanUtils.toBean(pageResult, IotProductScriptRespVO.class));
+    }
+
+    @PostMapping("/test")
+    @Operation(summary = "测试产品脚本")
+    @PreAuthorize("@ss.hasPermission('iot:product-script:test')")
+    public CommonResult<IotProductScriptTestRespVO> testProductScript(
+            @Valid @RequestBody IotProductScriptTestReqVO testReqVO) {
+        return success(productScriptService.testProductScript(testReqVO));
+    }
+
+    @PutMapping("/update-status")
+    @Operation(summary = "更新产品脚本状态")
+    @PreAuthorize("@ss.hasPermission('iot:product-script:update')")
+    public CommonResult<Boolean> updateProductScriptStatus(
+            @Valid @RequestBody IotProductScriptUpdateStatusReqVO updateStatusReqVO) {
+        productScriptService.updateProductScriptStatus(updateStatusReqVO.getId(), updateStatusReqVO.getStatus());
+        return success(true);
+    }
+}

+ 46 - 0
yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/product/vo/script/IotProductScriptPageReqVO.java

@@ -0,0 +1,46 @@
+package cn.iocoder.yudao.module.iot.controller.admin.product.vo.script;
+
+import cn.iocoder.yudao.framework.common.pojo.PageParam;
+import io.swagger.v3.oas.annotations.media.Schema;
+import lombok.Data;
+import lombok.EqualsAndHashCode;
+import lombok.ToString;
+import org.springframework.format.annotation.DateTimeFormat;
+
+import java.time.LocalDateTime;
+
+import static cn.iocoder.yudao.framework.common.util.date.DateUtils.FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND;
+
+@Schema(description = "管理后台 - IoT 产品脚本信息分页 Request VO")
+@Data
+@EqualsAndHashCode(callSuper = true)
+@ToString(callSuper = true)
+public class IotProductScriptPageReqVO extends PageParam {
+
+    @Schema(description = "产品ID", example = "28277")
+    private Long productId;
+
+    @Schema(description = "产品唯一标识符")
+    private String productKey;
+
+    @Schema(description = "脚本类型(property_parser=属性解析,event_parser=事件解析,command_encoder=命令编码)", example = "2")
+    private String scriptType;
+
+    @Schema(description = "脚本语言")
+    private String scriptLanguage;
+
+    @Schema(description = "状态(0=禁用 1=启用)", example = "2")
+    private Integer status;
+
+    @Schema(description = "备注说明", example = "你说的对")
+    private String remark;
+
+    @Schema(description = "最后测试时间")
+    @DateTimeFormat(pattern = FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND)
+    private LocalDateTime[] lastTestTime;
+
+    @Schema(description = "创建时间")
+    @DateTimeFormat(pattern = FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND)
+    private LocalDateTime[] createTime;
+
+}

+ 63 - 0
yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/product/vo/script/IotProductScriptRespVO.java

@@ -0,0 +1,63 @@
+package cn.iocoder.yudao.module.iot.controller.admin.product.vo.script;
+
+import com.alibaba.excel.annotation.ExcelIgnoreUnannotated;
+import com.alibaba.excel.annotation.ExcelProperty;
+import io.swagger.v3.oas.annotations.media.Schema;
+import lombok.Data;
+
+import java.time.LocalDateTime;
+
+@Schema(description = "管理后台 - IoT 产品脚本信息 Response VO")
+@Data
+@ExcelIgnoreUnannotated
+public class IotProductScriptRespVO {
+
+    @Schema(description = "主键", requiredMode = Schema.RequiredMode.REQUIRED, example = "26565")
+    @ExcelProperty("主键")
+    private Long id;
+
+    @Schema(description = "产品ID", requiredMode = Schema.RequiredMode.REQUIRED, example = "28277")
+    @ExcelProperty("产品ID")
+    private Long productId;
+
+    @Schema(description = "产品唯一标识符", requiredMode = Schema.RequiredMode.REQUIRED)
+    @ExcelProperty("产品唯一标识符")
+    private String productKey;
+
+    @Schema(description = "脚本类型(property_parser=属性解析,event_parser=事件解析,command_encoder=命令编码)", requiredMode = Schema.RequiredMode.REQUIRED, example = "2")
+    @ExcelProperty("脚本类型(property_parser=属性解析,event_parser=事件解析,command_encoder=命令编码)")
+    private String scriptType;
+
+    @Schema(description = "脚本内容", requiredMode = Schema.RequiredMode.REQUIRED)
+    @ExcelProperty("脚本内容")
+    private String scriptContent;
+
+    @Schema(description = "脚本语言", requiredMode = Schema.RequiredMode.REQUIRED)
+    @ExcelProperty("脚本语言")
+    private String scriptLanguage;
+
+    @Schema(description = "状态(0=禁用 1=启用)", requiredMode = Schema.RequiredMode.REQUIRED, example = "2")
+    @ExcelProperty("状态(0=禁用 1=启用)")
+    private Integer status;
+
+    @Schema(description = "备注说明", example = "你说的对")
+    @ExcelProperty("备注说明")
+    private String remark;
+
+    @Schema(description = "最后测试时间")
+    @ExcelProperty("最后测试时间")
+    private LocalDateTime lastTestTime;
+
+    @Schema(description = "最后测试结果(0=失败 1=成功)")
+    @ExcelProperty("最后测试结果(0=失败 1=成功)")
+    private Integer lastTestResult;
+
+    @Schema(description = "脚本版本号", requiredMode = Schema.RequiredMode.REQUIRED)
+    @ExcelProperty("脚本版本号")
+    private Integer version;
+
+    @Schema(description = "创建时间", requiredMode = Schema.RequiredMode.REQUIRED)
+    @ExcelProperty("创建时间")
+    private LocalDateTime createTime;
+
+}

+ 42 - 0
yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/product/vo/script/IotProductScriptSaveReqVO.java

@@ -0,0 +1,42 @@
+package cn.iocoder.yudao.module.iot.controller.admin.product.vo.script;
+
+import io.swagger.v3.oas.annotations.media.Schema;
+import jakarta.validation.constraints.NotEmpty;
+import jakarta.validation.constraints.NotNull;
+import lombok.Data;
+
+@Schema(description = "管理后台 - IoT 产品脚本信息新增/修改 Request VO")
+@Data
+public class IotProductScriptSaveReqVO {
+
+    @Schema(description = "主键", requiredMode = Schema.RequiredMode.REQUIRED, example = "26565")
+    private Long id;
+
+    @Schema(description = "产品ID", requiredMode = Schema.RequiredMode.REQUIRED, example = "28277")
+    @NotNull(message = "产品ID不能为空")
+    private Long productId;
+
+    @Schema(description = "产品唯一标识符", requiredMode = Schema.RequiredMode.REQUIRED)
+    @NotEmpty(message = "产品唯一标识符不能为空")
+    private String productKey;
+
+    @Schema(description = "脚本类型(property_parser=属性解析,event_parser=事件解析,command_encoder=命令编码)", requiredMode = Schema.RequiredMode.REQUIRED, example = "2")
+    @NotEmpty(message = "脚本类型(property_parser=属性解析,event_parser=事件解析,command_encoder=命令编码)不能为空")
+    private String scriptType;
+
+    @Schema(description = "脚本内容", requiredMode = Schema.RequiredMode.REQUIRED)
+    @NotEmpty(message = "脚本内容不能为空")
+    private String scriptContent;
+
+    @Schema(description = "脚本语言", requiredMode = Schema.RequiredMode.REQUIRED)
+    @NotEmpty(message = "脚本语言不能为空")
+    private String scriptLanguage;
+
+    @Schema(description = "状态(0=禁用 1=启用)", requiredMode = Schema.RequiredMode.REQUIRED, example = "2")
+    @NotNull(message = "状态(0=禁用 1=启用)不能为空")
+    private Integer status;
+
+    @Schema(description = "备注说明", example = "你说的对")
+    private String remark;
+
+}

+ 35 - 0
yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/product/vo/script/IotProductScriptTestReqVO.java

@@ -0,0 +1,35 @@
+package cn.iocoder.yudao.module.iot.controller.admin.product.vo.script;
+
+import io.swagger.v3.oas.annotations.media.Schema;
+import jakarta.validation.constraints.NotEmpty;
+import jakarta.validation.constraints.NotNull;
+import lombok.Data;
+
+@Schema(description = "管理后台 - IoT 产品脚本测试 Request VO")
+@Data
+public class IotProductScriptTestReqVO {
+
+    @Schema(description = "脚本ID,如果已保存脚本则传入", example = "1024")
+    private Long id;
+
+    @Schema(description = "产品ID", requiredMode = Schema.RequiredMode.REQUIRED, example = "2048")
+    @NotNull(message = "产品ID不能为空")
+    private Long productId;
+
+    @Schema(description = "脚本类型", requiredMode = Schema.RequiredMode.REQUIRED, example = "property_parser")
+    @NotEmpty(message = "脚本类型不能为空")
+    private String scriptType;
+
+    @Schema(description = "脚本内容", requiredMode = Schema.RequiredMode.REQUIRED)
+    @NotEmpty(message = "脚本内容不能为空")
+    private String scriptContent;
+
+    @Schema(description = "脚本语言", requiredMode = Schema.RequiredMode.REQUIRED, example = "javascript")
+    @NotEmpty(message = "脚本语言不能为空")
+    private String scriptLanguage;
+
+    @Schema(description = "测试输入数据", requiredMode = Schema.RequiredMode.REQUIRED)
+    @NotEmpty(message = "测试输入数据不能为空")
+    private String testInput;
+
+}

+ 39 - 0
yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/product/vo/script/IotProductScriptTestRespVO.java

@@ -0,0 +1,39 @@
+package cn.iocoder.yudao.module.iot.controller.admin.product.vo.script;
+
+import io.swagger.v3.oas.annotations.media.Schema;
+import lombok.Data;
+
+@Schema(description = "管理后台 - IoT 产品脚本测试 Response VO")
+@Data
+public class IotProductScriptTestRespVO {
+
+    @Schema(description = "测试是否成功", requiredMode = Schema.RequiredMode.REQUIRED)
+    private Boolean success;
+
+    @Schema(description = "测试结果输出")
+    private Object output;
+
+    @Schema(description = "错误消息,失败时返回")
+    private String errorMessage;
+
+    @Schema(description = "执行耗时(毫秒)")
+    private Long executionTimeMs;
+
+    // 静态工厂方法 - 成功
+    public static IotProductScriptTestRespVO success(Object output, Long executionTimeMs) {
+        IotProductScriptTestRespVO respVO = new IotProductScriptTestRespVO();
+        respVO.setSuccess(true);
+        respVO.setOutput(output);
+        respVO.setExecutionTimeMs(executionTimeMs);
+        return respVO;
+    }
+
+    // 静态工厂方法 - 失败
+    public static IotProductScriptTestRespVO error(String errorMessage, Long executionTimeMs) {
+        IotProductScriptTestRespVO respVO = new IotProductScriptTestRespVO();
+        respVO.setSuccess(false);
+        respVO.setErrorMessage(errorMessage);
+        respVO.setExecutionTimeMs(executionTimeMs);
+        return respVO;
+    }
+}

+ 19 - 0
yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/product/vo/script/IotProductScriptUpdateStatusReqVO.java

@@ -0,0 +1,19 @@
+package cn.iocoder.yudao.module.iot.controller.admin.product.vo.script;
+
+import io.swagger.v3.oas.annotations.media.Schema;
+import jakarta.validation.constraints.NotNull;
+import lombok.Data;
+
+@Schema(description = "管理后台 - IoT 产品脚本状态更新 Request VO")
+@Data
+public class IotProductScriptUpdateStatusReqVO {
+
+    @Schema(description = "脚本ID", requiredMode = Schema.RequiredMode.REQUIRED, example = "1024")
+    @NotNull(message = "脚本ID不能为空")
+    private Long id;
+
+    @Schema(description = "状态(0=禁用 1=启用)", requiredMode = Schema.RequiredMode.REQUIRED, example = "1")
+    @NotNull(message = "状态不能为空")
+    private Integer status;
+
+}

+ 72 - 0
yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/dataobject/product/IotProductScriptDO.java

@@ -0,0 +1,72 @@
+package cn.iocoder.yudao.module.iot.dal.dataobject.product;
+
+import cn.iocoder.yudao.framework.mybatis.core.dataobject.BaseDO;
+import com.baomidou.mybatisplus.annotation.KeySequence;
+import com.baomidou.mybatisplus.annotation.TableId;
+import com.baomidou.mybatisplus.annotation.TableName;
+import lombok.*;
+
+import java.time.LocalDateTime;
+
+/**
+ * IoT 产品脚本信息 DO
+ *
+ * @author 芋道源码
+ */
+@TableName("iot_product_script")
+@KeySequence("iot_product_script_seq") // 用于 Oracle、PostgreSQL、Kingbase、DB2、H2 数据库的主键自增。如果是 MySQL 等数据库,可不写。
+@Data
+@EqualsAndHashCode(callSuper = true)
+@ToString(callSuper = true)
+@Builder
+@NoArgsConstructor
+@AllArgsConstructor
+public class IotProductScriptDO extends BaseDO {
+
+    /**
+     * 主键
+     */
+    @TableId
+    private Long id;
+    /**
+     * 产品ID
+     */
+    private Long productId;
+    /**
+     * 产品唯一标识符
+     */
+    private String productKey;
+    /**
+     * 脚本类型(property_parser=属性解析,event_parser=事件解析,command_encoder=命令编码)
+     */
+    private String scriptType;
+    /**
+     * 脚本内容
+     */
+    private String scriptContent;
+    /**
+     * 脚本语言
+     */
+    private String scriptLanguage;
+    /**
+     * 状态(0=禁用 1=启用)
+     */
+    private Integer status;
+    /**
+     * 备注说明
+     */
+    private String remark;
+    /**
+     * 最后测试时间
+     */
+    private LocalDateTime lastTestTime;
+    /**
+     * 最后测试结果(0=失败 1=成功)
+     */
+    private Integer lastTestResult;
+    /**
+     * 脚本版本号
+     */
+    private Integer version;
+
+}

+ 31 - 0
yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/mysql/product/IotProductScriptMapper.java

@@ -0,0 +1,31 @@
+package cn.iocoder.yudao.module.iot.dal.mysql.product;
+
+import cn.iocoder.yudao.framework.common.pojo.PageResult;
+import cn.iocoder.yudao.framework.mybatis.core.mapper.BaseMapperX;
+import cn.iocoder.yudao.framework.mybatis.core.query.LambdaQueryWrapperX;
+import cn.iocoder.yudao.module.iot.controller.admin.product.vo.script.IotProductScriptPageReqVO;
+import cn.iocoder.yudao.module.iot.dal.dataobject.product.IotProductScriptDO;
+import org.apache.ibatis.annotations.Mapper;
+
+/**
+ * IoT 产品脚本信息 Mapper
+ *
+ * @author 芋道源码
+ */
+@Mapper
+public interface IotProductScriptMapper extends BaseMapperX<IotProductScriptDO> {
+
+    default PageResult<IotProductScriptDO> selectPage(IotProductScriptPageReqVO reqVO) {
+        return selectPage(reqVO, new LambdaQueryWrapperX<IotProductScriptDO>()
+                .eqIfPresent(IotProductScriptDO::getProductId, reqVO.getProductId())
+                .eqIfPresent(IotProductScriptDO::getProductKey, reqVO.getProductKey())
+                .eqIfPresent(IotProductScriptDO::getScriptType, reqVO.getScriptType())
+                .eqIfPresent(IotProductScriptDO::getScriptLanguage, reqVO.getScriptLanguage())
+                .eqIfPresent(IotProductScriptDO::getStatus, reqVO.getStatus())
+                .eqIfPresent(IotProductScriptDO::getRemark, reqVO.getRemark())
+                .betweenIfPresent(IotProductScriptDO::getLastTestTime, reqVO.getLastTestTime())
+                .betweenIfPresent(IotProductScriptDO::getCreateTime, reqVO.getCreateTime())
+                .orderByDesc(IotProductScriptDO::getId));
+    }
+
+}

+ 82 - 0
yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/product/IotProductScriptService.java

@@ -0,0 +1,82 @@
+package cn.iocoder.yudao.module.iot.service.product;
+
+import cn.iocoder.yudao.framework.common.pojo.PageResult;
+import cn.iocoder.yudao.module.iot.controller.admin.product.vo.script.IotProductScriptPageReqVO;
+import cn.iocoder.yudao.module.iot.controller.admin.product.vo.script.IotProductScriptSaveReqVO;
+import cn.iocoder.yudao.module.iot.controller.admin.product.vo.script.IotProductScriptTestReqVO;
+import cn.iocoder.yudao.module.iot.controller.admin.product.vo.script.IotProductScriptTestRespVO;
+import cn.iocoder.yudao.module.iot.dal.dataobject.product.IotProductScriptDO;
+import jakarta.validation.Valid;
+
+import java.util.List;
+
+/**
+ * IoT 产品脚本信息 Service 接口
+ *
+ * @author 芋道源码
+ */
+public interface IotProductScriptService {
+
+    /**
+     * 创建IoT 产品脚本信息
+     *
+     * @param createReqVO 创建信息
+     * @return 编号
+     */
+    Long createProductScript(@Valid IotProductScriptSaveReqVO createReqVO);
+
+    /**
+     * 更新IoT 产品脚本信息
+     *
+     * @param updateReqVO 更新信息
+     */
+    void updateProductScript(@Valid IotProductScriptSaveReqVO updateReqVO);
+
+    /**
+     * 删除IoT 产品脚本信息
+     *
+     * @param id 编号
+     */
+    void deleteProductScript(Long id);
+
+    /**
+     * 获得IoT 产品脚本信息
+     *
+     * @param id 编号
+     * @return IoT 产品脚本信息
+     */
+    IotProductScriptDO getProductScript(Long id);
+
+    /**
+     * 获得IoT 产品脚本信息分页
+     *
+     * @param pageReqVO 分页查询
+     * @return IoT 产品脚本信息分页
+     */
+    PageResult<IotProductScriptDO> getProductScriptPage(IotProductScriptPageReqVO pageReqVO);
+
+    /**
+     * 获取产品的脚本列表
+     *
+     * @param productId 产品ID
+     * @return 脚本列表
+     */
+    List<IotProductScriptDO> getProductScriptListByProductId(Long productId);
+
+    /**
+     * 测试产品脚本
+     *
+     * @param testReqVO 测试请求
+     * @return 测试结果
+     */
+    IotProductScriptTestRespVO testProductScript(@Valid IotProductScriptTestReqVO testReqVO);
+
+    /**
+     * 更新产品脚本状态
+     *
+     * @param id     脚本ID
+     * @param status 状态
+     */
+    void updateProductScriptStatus(Long id, Integer status);
+
+}

+ 219 - 0
yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/product/IotProductScriptServiceImpl.java

@@ -0,0 +1,219 @@
+package cn.iocoder.yudao.module.iot.service.product;
+
+import cn.iocoder.yudao.framework.common.pojo.PageResult;
+import cn.iocoder.yudao.framework.common.util.object.BeanUtils;
+import cn.iocoder.yudao.module.iot.controller.admin.product.vo.script.IotProductScriptPageReqVO;
+import cn.iocoder.yudao.module.iot.controller.admin.product.vo.script.IotProductScriptSaveReqVO;
+import cn.iocoder.yudao.module.iot.controller.admin.product.vo.script.IotProductScriptTestReqVO;
+import cn.iocoder.yudao.module.iot.controller.admin.product.vo.script.IotProductScriptTestRespVO;
+import cn.iocoder.yudao.module.iot.dal.dataobject.product.IotProductDO;
+import cn.iocoder.yudao.module.iot.dal.dataobject.product.IotProductScriptDO;
+import cn.iocoder.yudao.module.iot.dal.mysql.product.IotProductScriptMapper;
+import cn.iocoder.yudao.module.iot.plugin.script.context.PluginScriptContext;
+import cn.iocoder.yudao.module.iot.plugin.script.service.ScriptService;
+import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
+import jakarta.annotation.Resource;
+import lombok.extern.slf4j.Slf4j;
+import org.springframework.stereotype.Service;
+import org.springframework.validation.annotation.Validated;
+
+import java.time.LocalDateTime;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception;
+import static cn.iocoder.yudao.module.iot.enums.ErrorCodeConstants.PRODUCT_NOT_EXISTS;
+import static cn.iocoder.yudao.module.iot.enums.ErrorCodeConstants.PRODUCT_SCRIPT_NOT_EXISTS;
+
+/**
+ * IoT 产品脚本信息 Service 实现类
+ *
+ * @author 芋道源码
+ */
+@Service
+@Validated
+@Slf4j
+public class IotProductScriptServiceImpl implements IotProductScriptService {
+
+    @Resource
+    private IotProductScriptMapper productScriptMapper;
+
+    @Resource
+    private IotProductService productService;
+
+    @Resource
+    private ScriptService scriptService;
+
+    @Override
+    public Long createProductScript(IotProductScriptSaveReqVO createReqVO) {
+        // 验证产品是否存在
+        validateProductExists(createReqVO.getProductId());
+
+        // 插入
+        IotProductScriptDO productScript = BeanUtils.toBean(createReqVO, IotProductScriptDO.class);
+        // 初始化版本为1
+        productScript.setVersion(1);
+        // 初始化测试相关字段
+        productScript.setLastTestResult(null);
+        productScript.setLastTestTime(null);
+        productScriptMapper.insert(productScript);
+        // 返回
+        return productScript.getId();
+    }
+
+    @Override
+    public void updateProductScript(IotProductScriptSaveReqVO updateReqVO) {
+        // 校验存在
+        validateProductScriptExists(updateReqVO.getId());
+
+        // 获取旧的记录,保留版本号和测试信息
+        IotProductScriptDO oldScript = getProductScript(updateReqVO.getId());
+
+        // 更新
+        IotProductScriptDO updateObj = BeanUtils.toBean(updateReqVO, IotProductScriptDO.class);
+        // 更新版本号
+        updateObj.setVersion(oldScript.getVersion() + 1);
+        // 保留测试相关信息
+        updateObj.setLastTestTime(oldScript.getLastTestTime());
+        updateObj.setLastTestResult(oldScript.getLastTestResult());
+        productScriptMapper.updateById(updateObj);
+    }
+
+    @Override
+    public void deleteProductScript(Long id) {
+        // 校验存在
+        validateProductScriptExists(id);
+        // 删除
+        productScriptMapper.deleteById(id);
+    }
+
+    private void validateProductScriptExists(Long id) {
+        if (productScriptMapper.selectById(id) == null) {
+            throw exception(PRODUCT_SCRIPT_NOT_EXISTS);
+        }
+    }
+
+    private void validateProductExists(Long productId) {
+        IotProductDO product = productService.getProduct(productId);
+        if (product == null) {
+            throw exception(PRODUCT_NOT_EXISTS);
+        }
+    }
+
+    @Override
+    public IotProductScriptDO getProductScript(Long id) {
+        return productScriptMapper.selectById(id);
+    }
+
+    @Override
+    public PageResult<IotProductScriptDO> getProductScriptPage(IotProductScriptPageReqVO pageReqVO) {
+        return productScriptMapper.selectPage(pageReqVO);
+    }
+
+    @Override
+    public List<IotProductScriptDO> getProductScriptListByProductId(Long productId) {
+        return productScriptMapper.selectList(new LambdaQueryWrapper<IotProductScriptDO>()
+                .eq(IotProductScriptDO::getProductId, productId)
+                .orderByDesc(IotProductScriptDO::getId));
+    }
+
+    @Override
+    public IotProductScriptTestRespVO testProductScript(IotProductScriptTestReqVO testReqVO) {
+        long startTime = System.currentTimeMillis();
+
+        try {
+            // 验证产品是否存在
+            validateProductExists(testReqVO.getProductId());
+
+            // 根据ID获取已保存的脚本(如果有)
+            IotProductScriptDO existingScript = null;
+            if (testReqVO.getId() != null) {
+                existingScript = getProductScript(testReqVO.getId());
+            }
+
+            // 创建测试上下文
+            PluginScriptContext context = new PluginScriptContext();
+            IotProductDO product = productService.getProduct(testReqVO.getProductId());
+
+            // 设置设备上下文(使用产品信息,没有具体设备)
+            context.withDeviceContext(product.getProductKey(), null);
+
+            // 设置输入参数
+            Map<String, Object> params = new HashMap<>();
+            params.put("input", testReqVO.getTestInput());
+            params.put("productKey", product.getProductKey());
+            params.put("scriptType", testReqVO.getScriptType());
+
+            // 根据脚本类型设置特定参数
+            switch (testReqVO.getScriptType()) {
+                case "property_parser":
+                    params.put("method", "property");
+                    break;
+                case "event_parser":
+                    params.put("method", "event");
+                    params.put("identifier", "default");
+                    break;
+                case "command_encoder":
+                    params.put("method", "command");
+                    break;
+                default:
+                    // 默认不添加额外参数
+            }
+
+            // 添加所有参数到上下文
+            for (Map.Entry<String, Object> entry : params.entrySet()) {
+                context.setParameter(entry.getKey(), entry.getValue());
+            }
+
+            // 执行脚本
+            Object result = scriptService.executeScript(
+                    testReqVO.getScriptLanguage(),
+                    testReqVO.getScriptContent(),
+                    context);
+
+            // 更新测试结果(如果是已保存的脚本)
+            if (existingScript != null) {
+                IotProductScriptDO updateObj = new IotProductScriptDO();
+                updateObj.setId(existingScript.getId());
+                updateObj.setLastTestTime(LocalDateTime.now());
+                updateObj.setLastTestResult(1); // 1表示成功
+                productScriptMapper.updateById(updateObj);
+            }
+
+            long executionTime = System.currentTimeMillis() - startTime;
+            return IotProductScriptTestRespVO.success(result, executionTime);
+
+        } catch (Exception e) {
+            log.error("[testProductScript][测试脚本异常]", e);
+
+            // 如果是已保存的脚本,更新测试失败状态
+            if (testReqVO.getId() != null) {
+                try {
+                    IotProductScriptDO updateObj = new IotProductScriptDO();
+                    updateObj.setId(testReqVO.getId());
+                    updateObj.setLastTestTime(LocalDateTime.now());
+                    updateObj.setLastTestResult(0); // 0表示失败
+                    productScriptMapper.updateById(updateObj);
+                } catch (Exception ex) {
+                    log.error("[testProductScript][更新脚本测试结果异常]", ex);
+                }
+            }
+
+            long executionTime = System.currentTimeMillis() - startTime;
+            return IotProductScriptTestRespVO.error(e.getMessage(), executionTime);
+        }
+    }
+
+    @Override
+    public void updateProductScriptStatus(Long id, Integer status) {
+        // 校验存在
+        validateProductScriptExists(id);
+
+        // 更新状态
+        IotProductScriptDO updateObj = new IotProductScriptDO();
+        updateObj.setId(id);
+        updateObj.setStatus(status);
+        productScriptMapper.updateById(updateObj);
+    }
+}