Jelajahi Sumber

reactor:【IoT 物联网】移除 script 脚本,简化逻辑复杂度

YunaiV 5 bulan lalu
induk
melakukan
cf52a16f6c
31 mengubah file dengan 7 tambahan dan 2815 penghapusan
  1. 0 40
      yudao-module-iot/yudao-module-iot-biz/pom.xml
  2. 0 61
      yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/ScriptTest.java
  3. 0 127
      yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/product/IotProductScriptController.java
  4. 0 53
      yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/product/vo/script/IotProductScriptPageReqVO.java
  5. 0 63
      yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/product/vo/script/IotProductScriptRespVO.java
  6. 0 49
      yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/product/vo/script/IotProductScriptSaveReqVO.java
  7. 0 38
      yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/product/vo/script/IotProductScriptTestReqVO.java
  8. 0 39
      yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/product/vo/script/IotProductScriptTestRespVO.java
  9. 0 22
      yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/product/vo/script/IotProductScriptUpdateStatusReqVO.java
  10. 0 73
      yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/dataobject/product/IotProductScriptDO.java
  11. 0 31
      yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/mysql/product/IotProductScriptMapper.java
  12. 0 113
      yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/script/ScriptExample.java
  13. 0 24
      yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/script/config/ScriptConfiguration.java
  14. 0 46
      yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/script/context/DefaultScriptContext.java
  15. 0 92
      yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/script/context/DeviceScriptContext.java
  16. 0 47
      yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/script/context/ScriptContext.java
  17. 0 49
      yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/script/engine/AbstractScriptEngine.java
  18. 0 348
      yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/script/engine/JsScriptEngine.java
  19. 0 25
      yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/script/engine/ScriptEngine.java
  20. 0 85
      yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/script/engine/ScriptEngineFactory.java
  21. 0 209
      yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/script/example/GraalJsExample.java
  22. 0 174
      yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/script/example/ProductScriptSamples.java
  23. 0 4
      yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/script/package-info.java
  24. 0 330
      yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/script/sandbox/JsSandbox.java
  25. 0 22
      yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/script/sandbox/ScriptSandbox.java
  26. 0 58
      yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/script/service/ScriptService.java
  27. 0 111
      yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/script/service/ScriptServiceImpl.java
  28. 0 159
      yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/script/util/ScriptUtils.java
  29. 0 82
      yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/product/IotProductScriptService.java
  30. 0 234
      yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/product/IotProductScriptServiceImpl.java
  31. 7 7
      yudao-server/src/main/resources/application-local.yaml

+ 0 - 40
yudao-module-iot/yudao-module-iot-biz/pom.xml

@@ -103,39 +103,6 @@
             <optional>true</optional>
         </dependency>
 
-        <!-- JavaScript 引擎 - 使用 GraalJS 替代 Nashorn -->
-        <!-- TODO @haohao:得考虑下,jdk8 可能不支持 graalvm,后续哈;【优先级:低】 -->
-        <dependency>
-            <groupId>org.graalvm.sdk</groupId>
-            <artifactId>graal-sdk</artifactId>
-            <version>22.3.0</version>
-        </dependency>
-        <dependency>
-            <groupId>org.graalvm.js</groupId>
-            <artifactId>js</artifactId>
-            <version>22.3.0</version>
-        </dependency>
-        <dependency>
-            <groupId>org.graalvm.js</groupId>
-            <artifactId>js-scriptengine</artifactId>
-            <version>22.3.0</version>
-        </dependency>
-
-        <!-- TODO @芋艿:合理注释 -->
-        <!-- IoT 数据桥梁的执行器所需消息队列。如果您只需要使用 rocketmq 那么则注释掉其它消息队列即可 -->
-        <!--        <dependency>-->
-        <!--            <groupId>org.apache.rocketmq</groupId>-->
-        <!--            <artifactId>rocketmq-spring-boot-starter</artifactId>-->
-        <!--        </dependency>-->
-        <!--        <dependency>-->
-        <!--            <groupId>org.springframework.kafka</groupId>-->
-        <!--            <artifactId>spring-kafka</artifactId>-->
-        <!--        </dependency>-->
-        <!--        <dependency>-->
-        <!--            <groupId>org.springframework.boot</groupId>-->
-        <!--            <artifactId>spring-boot-starter-amqp</artifactId>-->
-        <!--        </dependency>-->
-
         <!-- IoT 网络组件:接收来自设备的上行数据 -->
         <!--        <dependency>-->
         <!--            <groupId>cn.iocoder.boot</groupId>-->
@@ -147,13 +114,6 @@
         <!--            <artifactId>yudao-module-iot-net-component-emqx</artifactId>-->
         <!--            <version>${revision}</version>-->
         <!--        </dependency>-->
-
-        <!-- 脚本相关 -->
-<!--        <dependency>-->
-<!--            <groupId>cn.iocoder.boot</groupId>-->
-<!--            <artifactId>yudao-module-iot-script</artifactId>-->
-<!--            <version>${revision}</version>-->
-<!--        </dependency>-->
     </dependencies>
 
 </project>

+ 0 - 61
yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/ScriptTest.java

@@ -1,61 +0,0 @@
-package cn.iocoder.yudao.module.iot;
-
-import cn.hutool.script.ScriptUtil;
-import javax.script.Bindings;
-import javax.script.ScriptEngine;
-import javax.script.ScriptException;
-
-/**
- * TODO 芋艿:测试脚本的接入
- */
-public class ScriptTest {
-
-    public static void main2(String[] args) {
-        // 创建一个 Groovy 脚本引擎
-        ScriptEngine engine = ScriptUtil.createGroovyEngine();
-
-        // 创建绑定参数
-        Bindings bindings = engine.createBindings();
-        bindings.put("name", "Alice");
-        bindings.put("age", 30);
-
-        // 定义一个稍微复杂的 Groovy 脚本
-        String script = "def greeting = 'Hello, ' + name + '!';\n" +
-                "def ageInFiveYears = age + 5;\n" +
-                "def message = greeting + ' In five years, you will be ' + ageInFiveYears + ' years old.';\n" +
-                "return message.toUpperCase();\n";
-
-        try {
-            // 执行脚本并获取结果
-            Object result = engine.eval(script, bindings);
-            System.out.println(result); // 输出: HELLO, ALICE! IN FIVE YEARS, YOU WILL BE 35 YEARS OLD.
-        } catch (ScriptException e) {
-            e.printStackTrace();
-        }
-    }
-
-    public static void main(String[] args) {
-        // 创建一个 JavaScript 脚本引擎
-        ScriptEngine jsEngine = ScriptUtil.createJsEngine();
-
-        // 创建绑定参数
-        Bindings jsBindings = jsEngine.createBindings();
-        jsBindings.put("name", "Bob");
-        jsBindings.put("age", 25);
-
-        // 定义一个简单的 JavaScript 脚本
-        String jsScript = "var greeting = 'Hello, ' + name + '!';\n" +
-                "var ageInTenYears = age + 10;\n" +
-                "var message = greeting + ' In ten years, you will be ' + ageInTenYears + ' years old.';\n" +
-                "message.toUpperCase();\n";
-
-        try {
-            // 执行脚本并获取结果
-            Object jsResult = jsEngine.eval(jsScript, jsBindings);
-            System.out.println(jsResult); // 输出: HELLO, BOB! IN TEN YEARS, YOU WILL BE 35 YEARS OLD.
-        } catch (ScriptException e) {
-            e.printStackTrace();
-        }
-    }
-
-}

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

@@ -1,127 +0,0 @@
-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.script.example.ProductScriptSamples;
-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;
-
-    @Resource
-    private ProductScriptSamples scriptSamples;
-
-    @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);
-    }
-
-    @GetMapping("/sample")
-    @Operation(summary = "获取示例脚本")
-    @Parameter(name = "type", description = "脚本类型(1=属性解析, 2=事件解析, 3=命令编码)", required = true, example = "1")
-    @PreAuthorize("@ss.hasPermission('iot:product-script:query')")
-    public CommonResult<String> getSampleScript(@RequestParam("type") Integer type) {
-        String sample;
-        // TODO @haohao:要不枚举下?
-        switch (type) {
-            case 1:
-                sample = scriptSamples.getPropertyParserSample();
-                break;
-            case 2:
-                sample = scriptSamples.getEventParserSample();
-                break;
-            case 3:
-                sample = scriptSamples.getCommandEncoderSample();
-                break;
-            default:
-                // TODO @haohao:不支持,返回 error 会不会好点哈?例如说,参数不正确;
-                sample = "// 不支持的脚本类型";
-        }
-        return success(sample);
-    }
-}

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

@@ -1,53 +0,0 @@
-package cn.iocoder.yudao.module.iot.controller.admin.product.vo.script;
-
-import cn.iocoder.yudao.framework.common.pojo.PageParam;
-import cn.iocoder.yudao.framework.common.validation.InEnum;
-import cn.iocoder.yudao.module.iot.enums.product.IotProductScriptLanguageEnum;
-import cn.iocoder.yudao.module.iot.enums.product.IotProductScriptStatusEnum;
-import cn.iocoder.yudao.module.iot.enums.product.IotProductScriptTypeEnum;
-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 = "脚本类型", example = "1")
-    @InEnum(IotProductScriptTypeEnum.class)
-    private Integer scriptType;
-
-    @Schema(description = "脚本语言")
-    @InEnum(IotProductScriptLanguageEnum.class)
-    private String scriptLanguage;
-
-    @Schema(description = "状态", example = "0")
-    @InEnum(IotProductScriptStatusEnum.class)
-    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;
-
-}

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

@@ -1,63 +0,0 @@
-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 = "脚本类型", requiredMode = Schema.RequiredMode.REQUIRED, example = "1")
-    @ExcelProperty("脚本类型")
-    private Integer scriptType;
-
-    @Schema(description = "脚本内容", requiredMode = Schema.RequiredMode.REQUIRED)
-    @ExcelProperty("脚本内容")
-    private String scriptContent;
-
-    @Schema(description = "脚本语言", requiredMode = Schema.RequiredMode.REQUIRED)
-    @ExcelProperty("脚本语言")
-    private String scriptLanguage;
-
-    @Schema(description = "状态", requiredMode = Schema.RequiredMode.REQUIRED, example = "0")
-    @ExcelProperty("状态")
-    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;
-
-}

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

@@ -1,49 +0,0 @@
-package cn.iocoder.yudao.module.iot.controller.admin.product.vo.script;
-
-import cn.iocoder.yudao.framework.common.validation.InEnum;
-import cn.iocoder.yudao.module.iot.enums.product.IotProductScriptLanguageEnum;
-import cn.iocoder.yudao.module.iot.enums.product.IotProductScriptStatusEnum;
-import cn.iocoder.yudao.module.iot.enums.product.IotProductScriptTypeEnum;
-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 = "脚本类型", requiredMode = Schema.RequiredMode.REQUIRED, example = "1")
-    @NotNull(message = "脚本类型不能为空")
-    @InEnum(IotProductScriptTypeEnum.class)
-    private Integer scriptType;
-
-    @Schema(description = "脚本内容", requiredMode = Schema.RequiredMode.REQUIRED)
-    @NotEmpty(message = "脚本内容不能为空")
-    private String scriptContent;
-
-    @Schema(description = "脚本语言", requiredMode = Schema.RequiredMode.REQUIRED)
-    @NotEmpty(message = "脚本语言不能为空")
-    @InEnum(IotProductScriptLanguageEnum.class)
-    private String scriptLanguage;
-
-    @Schema(description = "状态", requiredMode = Schema.RequiredMode.REQUIRED, example = "0")
-    @NotNull(message = "状态不能为空")
-    @InEnum(IotProductScriptStatusEnum.class)
-    private Integer status;
-
-    @Schema(description = "备注说明", example = "你说的对")
-    private String remark;
-
-}

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

@@ -1,38 +0,0 @@
-package cn.iocoder.yudao.module.iot.controller.admin.product.vo.script;
-
-import cn.iocoder.yudao.framework.common.validation.InEnum;
-import cn.iocoder.yudao.module.iot.enums.product.IotProductScriptTypeEnum;
-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 = "1")
-    @NotNull(message = "脚本类型不能为空")
-    @InEnum(value = IotProductScriptTypeEnum.class)
-    private Integer 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;
-
-}

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

@@ -1,39 +0,0 @@
-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;
-    }
-}

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

@@ -1,22 +0,0 @@
-package cn.iocoder.yudao.module.iot.controller.admin.product.vo.script;
-
-import cn.iocoder.yudao.framework.common.validation.InEnum;
-import cn.iocoder.yudao.module.iot.enums.product.IotProductScriptStatusEnum;
-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 = "状态", requiredMode = Schema.RequiredMode.REQUIRED, example = "0")
-    @NotNull(message = "状态不能为空")
-    @InEnum(IotProductScriptStatusEnum.class)
-    private Integer status;
-
-}

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

@@ -1,73 +0,0 @@
-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;
-
-// TODO @haohao:类似阿里云的脚本,貌似是一个?这个可以简化么?【微信讨论哈】类似阿里云,貌似是加了个 topic?
-/**
- * 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;
-
-}

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

@@ -1,31 +0,0 @@
-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));
-    }
-
-}

+ 0 - 113
yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/script/ScriptExample.java

@@ -1,113 +0,0 @@
-package cn.iocoder.yudao.module.iot.script;
-
-import cn.hutool.core.map.MapUtil;
-import cn.iocoder.yudao.module.iot.script.context.DefaultScriptContext;
-import cn.iocoder.yudao.module.iot.script.context.ScriptContext;
-import cn.iocoder.yudao.module.iot.script.service.ScriptService;
-import lombok.extern.slf4j.Slf4j;
-import org.springframework.beans.factory.annotation.Autowired;
-import org.springframework.stereotype.Component;
-
-import java.util.Map;
-
-// TODO @haohao:挪到 test 目录下
-/**
- * 脚本使用示例类
- */
-@Slf4j
-@Component
-public class ScriptExample {
-
-    @Autowired
-    private ScriptService scriptService;
-
-    /**
-     * 执行简单的 JavaScript 脚本
-     *
-     * @return 执行结果
-     */
-    public Object executeSimpleScript() {
-        // 简单的脚本内容
-        String script = "var result = a + b; result;";
-
-        // 创建参数
-        Map<String, Object> params = MapUtil.newHashMap();
-        params.put("a", 10);
-        params.put("b", 20);
-
-        // 执行脚本
-        return scriptService.executeJavaScript(script, params);
-    }
-
-    /**
-     * 执行包含函数的 JavaScript 脚本
-     *
-     * @return 执行结果
-     */
-    public Object executeScriptWithFunction() {
-        // 包含函数的脚本内容
-        String script = "function calc(x, y) { return x * y; } calc(a, b);";
-
-        // 创建上下文
-        ScriptContext context = new DefaultScriptContext();
-        context.setParameter("a", 5);
-        context.setParameter("b", 6);
-
-        // 执行脚本
-        return scriptService.executeJavaScript(script, context);
-    }
-
-    /**
-     * 执行包含工具类使用的脚本
-     *
-     * @return 执行结果
-     */
-    public Object executeScriptWithUtils() {
-        // 使用工具类的脚本内容
-        String script = "var data = {name: 'test', value: 123}; utils.toJson(data);";
-
-        // 执行脚本
-        return scriptService.executeJavaScript(script, MapUtil.newHashMap());
-    }
-
-    /**
-     * 执行包含日志输出的脚本
-     *
-     * @return 执行结果
-     */
-    public Object executeScriptWithLogging() {
-        // 包含日志输出的脚本内容
-        String script = "log.info('脚本开始执行...'); " +
-                "var result = a + b; " +
-                "log.info('计算结果: ' + result); " +
-                "result;";
-
-        // 创建参数
-        Map<String, Object> params = MapUtil.newHashMap();
-        params.put("a", 100);
-        params.put("b", 200);
-
-        // 执行脚本
-        return scriptService.executeJavaScript(script, params);
-    }
-
-    /**
-     * 演示脚本安全性验证
-     *
-     * @return 是否安全
-     */
-    public boolean validateScriptSecurity() {
-        // 安全的脚本
-        String safeScript = "var x = 10; var y = 20; x + y;";
-        boolean safeResult = scriptService.validateScript("js", safeScript);
-
-        // 不安全的脚本
-        String unsafeScript = "java.lang.System.exit(0);";
-        boolean unsafeResult = scriptService.validateScript("js", unsafeScript);
-
-        log.info("安全脚本验证结果: {}", safeResult);
-        log.info("不安全脚本验证结果: {}", unsafeResult);
-
-        return safeResult && !unsafeResult;
-    }
-}

+ 0 - 24
yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/script/config/ScriptConfiguration.java

@@ -1,24 +0,0 @@
-package cn.iocoder.yudao.module.iot.script.config;
-
-import cn.iocoder.yudao.module.iot.script.engine.ScriptEngineFactory;
-import org.springframework.context.annotation.Bean;
-import org.springframework.context.annotation.Configuration;
-import org.springframework.context.annotation.Primary;
-
-/**
- * 脚本模块配置类
- */
-@Configuration
-public class ScriptConfiguration {
-
-    /**
-     * 创建脚本引擎工厂
-     *
-     * @return 脚本引擎工厂
-     */
-    @Bean
-    @Primary
-    public ScriptEngineFactory scriptEngineFactory() {
-        return new ScriptEngineFactory();
-    }
-}

+ 0 - 46
yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/script/context/DefaultScriptContext.java

@@ -1,46 +0,0 @@
-package cn.iocoder.yudao.module.iot.script.context;
-
-import cn.hutool.core.map.MapUtil;
-
-import java.util.Map;
-
-/**
- * 默认脚本上下文实现
- */
-public class DefaultScriptContext implements ScriptContext {
-
-    /**
-     * 上下文参数
-     */
-    private final Map<String, Object> parameters = MapUtil.newHashMap();
-
-    /**
-     * 上下文函数
-     */
-    private final Map<String, Object> functions = MapUtil.newHashMap();
-
-    @Override
-    public Map<String, Object> getParameters() {
-        return parameters;
-    }
-
-    @Override
-    public Map<String, Object> getFunctions() {
-        return functions;
-    }
-
-    @Override
-    public void setParameter(String key, Object value) {
-        parameters.put(key, value);
-    }
-
-    @Override
-    public Object getParameter(String key) {
-        return parameters.get(key);
-    }
-
-    @Override
-    public void registerFunction(String name, Object function) {
-        functions.put(name, function);
-    }
-}

+ 0 - 92
yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/script/context/DeviceScriptContext.java

@@ -1,92 +0,0 @@
-package cn.iocoder.yudao.module.iot.script.context;
-
-import cn.hutool.core.map.MapUtil;
-import cn.hutool.core.util.StrUtil;
-import lombok.Getter;
-import lombok.extern.slf4j.Slf4j;
-
-import java.util.Map;
-
-/**
- * 设备脚本上下文,提供设备相关的上下文信息
- */
-@Slf4j
-public class DeviceScriptContext extends DefaultScriptContext {
-
-    /**
-     * 产品 Key
-     */
-    @Getter
-    private String productKey;
-
-    /**
-     * 设备名称
-     */
-    @Getter
-    private String deviceName;
-
-    /**
-     * 设备属性数据缓存
-     */
-    private Map<String, Object> properties;
-
-    /**
-     * 使用产品 Key 和设备名称初始化上下文
-     *
-     * @param productKey 产品 Key
-     * @param deviceName 设备名称,可以为 null
-     * @return 当前上下文实例,用于链式调用
-     */
-    public DeviceScriptContext withDeviceInfo(String productKey, String deviceName) {
-        this.productKey = productKey;
-        this.deviceName = deviceName;
-
-        // 添加到参数中,便于脚本访问
-        setParameter("productKey", productKey);
-        if (StrUtil.isNotEmpty(deviceName)) {
-            setParameter("deviceName", deviceName);
-        }
-        return this;
-    }
-
-    /**
-     * 设置设备属性数据
-     *
-     * @param properties 属性数据
-     * @return 当前上下文实例,用于链式调用
-     */
-    public DeviceScriptContext withProperties(Map<String, Object> properties) {
-        this.properties = properties;
-        if (MapUtil.isNotEmpty(properties)) {
-            setParameter("properties", properties);
-        }
-        return this;
-    }
-
-    /**
-     * 获取设备属性值
-     *
-     * @param key 属性标识符
-     * @return 属性值
-     */
-    public Object getProperty(String key) {
-        if (MapUtil.isEmpty(properties)) {
-            return null;
-        }
-        return properties.get(key);
-    }
-
-    /**
-     * 设置设备属性值
-     *
-     * @param key   属性标识符
-     * @param value 属性值
-     */
-    public void setProperty(String key, Object value) {
-        if (this.properties == null) {
-            this.properties = MapUtil.newHashMap();
-            setParameter("properties", this.properties);
-        }
-        this.properties.put(key, value);
-    }
-}

+ 0 - 47
yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/script/context/ScriptContext.java

@@ -1,47 +0,0 @@
-package cn.iocoder.yudao.module.iot.script.context;
-
-import java.util.Map;
-
-/**
- * 脚本上下文接口,定义脚本执行所需的上下文环境
- */
-public interface ScriptContext {
-
-    /**
-     * 获取上下文参数
-     *
-     * @return 上下文参数
-     */
-    Map<String, Object> getParameters();
-
-    /**
-     * 获取上下文函数
-     *
-     * @return 上下文函数
-     */
-    Map<String, Object> getFunctions();
-
-    /**
-     * 设置上下文参数
-     *
-     * @param key   参数名
-     * @param value 参数值
-     */
-    void setParameter(String key, Object value);
-
-    /**
-     * 获取上下文参数
-     *
-     * @param key 参数名
-     * @return 参数值
-     */
-    Object getParameter(String key);
-
-    /**
-     * 注册函数
-     *
-     * @param name     函数名称
-     * @param function 函数对象
-     */
-    void registerFunction(String name, Object function);
-}

+ 0 - 49
yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/script/engine/AbstractScriptEngine.java

@@ -1,49 +0,0 @@
-package cn.iocoder.yudao.module.iot.script.engine;
-
-import cn.iocoder.yudao.module.iot.script.context.ScriptContext;
-import cn.iocoder.yudao.module.iot.script.sandbox.ScriptSandbox;
-import lombok.extern.slf4j.Slf4j;
-
-/**
- * 抽象脚本引擎,提供脚本引擎的基本框架
- */
-@Slf4j
-public abstract class AbstractScriptEngine implements ScriptEngine {
-
-    /**
-     * 脚本沙箱,用于提供安全执行环境
-     */
-    protected final ScriptSandbox sandbox;
-
-    /**
-     * 构造函数
-     *
-     * @param sandbox 脚本沙箱
-     */
-    protected AbstractScriptEngine(ScriptSandbox sandbox) {
-        this.sandbox = sandbox;
-    }
-
-    @Override
-    public Object execute(String script, ScriptContext context) {
-        try {
-            // 执行前验证脚本安全性
-            sandbox.validate(script);
-            // 执行脚本
-            return doExecute(script, context);
-        } catch (Exception e) {
-            log.error("执行脚本出错:{}", e.getMessage(), e);
-            throw new RuntimeException("脚本执行失败:" + e.getMessage(), e);
-        }
-    }
-
-    /**
-     * 执行脚本的具体实现
-     *
-     * @param script  脚本内容
-     * @param context 脚本上下文
-     * @return 脚本执行结果
-     * @throws Exception 执行异常
-     */
-    protected abstract Object doExecute(String script, ScriptContext context) throws Exception;
-}

+ 0 - 348
yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/script/engine/JsScriptEngine.java

@@ -1,348 +0,0 @@
-package cn.iocoder.yudao.module.iot.script.engine;
-
-import cn.hutool.core.map.MapUtil;
-import cn.hutool.core.util.IdUtil;
-import cn.hutool.core.util.StrUtil;
-import cn.iocoder.yudao.module.iot.script.context.ScriptContext;
-import cn.iocoder.yudao.module.iot.script.sandbox.ScriptSandbox;
-import cn.iocoder.yudao.module.iot.script.util.ScriptUtils;
-import lombok.extern.slf4j.Slf4j;
-import org.graalvm.polyglot.*;
-
-import java.nio.file.Path;
-import java.util.Map;
-import java.util.concurrent.ConcurrentHashMap;
-import java.util.concurrent.TimeUnit;
-import java.util.concurrent.atomic.AtomicReference;
-
-/**
- * JavaScript 脚本引擎实现,基于 GraalJS Context API
- */
-@Slf4j
-public class JsScriptEngine extends AbstractScriptEngine implements AutoCloseable {
-
-    /**
-     * JavaScript 引擎类型
-     */
-    public static final String TYPE = "js";
-
-    /**
-     * 脚本语言类型
-     */
-    private static final String LANGUAGE_ID = "js";
-
-    /**
-     * GraalJS 上下文
-     */
-    private final Context context;
-
-    /**
-     * 脚本源代码缓存
-     */
-    private final Map<String, Source> sourceCache = new ConcurrentHashMap<>();
-
-    /**
-     * 脚本缓存的最大数量
-     */
-    private static final int MAX_CACHE_SIZE = 1000;
-
-    /**
-     * 构造函数
-     *
-     * @param sandbox JavaScript 沙箱
-     */
-    public JsScriptEngine(ScriptSandbox sandbox) {
-        super(sandbox);
-
-        // 创建安全的主机访问配置
-        HostAccess hostAccess = HostAccess.newBuilder()
-                .allowPublicAccess(true) // 允许访问公共方法和字段
-                .allowArrayAccess(true) // 允许数组访问
-                .allowListAccess(true) // 允许 List 访问
-                .allowMapAccess(true) // 允许 Map 访问
-                .build();
-
-        // 创建隔离的临时目录路径
-        // TODO @haohao:貌似没用到?
-        Path tempDirectory = Path.of(System.getProperty("java.io.tmpdir"), "graaljs-" + IdUtil.fastSimpleUUID());
-
-        // 初始化 GraalJS 上下文
-        this.context = Context.newBuilder(LANGUAGE_ID)
-                .allowHostAccess(hostAccess) // 使用安全的主机访问配置
-                .allowHostClassLookup(className -> false) // 禁止查找 Java 类
-                .allowIO(false) // 禁止文件 IO
-                .allowNativeAccess(false) // 禁止本地访问
-                .allowCreateThread(false) // 禁止创建线程
-                .allowEnvironmentAccess(EnvironmentAccess.NONE) // 禁止环境变量访问
-                .allowExperimentalOptions(false) // 禁止实验性选项
-                .option("js.ecmascript-version", "2021") // 使用最新的 ECMAScript 标准
-                .option("js.foreign-object-prototype", "false") // 禁用外部对象原型
-                .option("js.nashorn-compat", "false") // 关闭 Nashorn 兼容模式以获得更好性能
-                .build();
-    }
-
-    @Override
-    protected Object doExecute(String script, ScriptContext context) throws Exception {
-        if (StrUtil.isBlank(script)) {
-            return null;
-        }
-
-        try {
-            // 绑定上下文变量
-            bindContextVariables(context);
-
-            // 从缓存获取或创建脚本源
-            Source source = getOrCreateSource(script);
-
-            // 执行脚本并捕获结果,添加超时控制
-            // TODO @haohao:通过线程池 + future 会好点?
-            Value result;
-            Thread executionThread = Thread.currentThread();
-            Thread watchdogThread = new Thread(() -> {
-                try {
-                    // 等待 5 秒
-                    TimeUnit.SECONDS.sleep(5);
-                    // 如果执行线程还在运行,中断它
-                    if (executionThread.isAlive()) {
-                        log.warn("脚本执行超时,强制中断");
-                        executionThread.interrupt();
-                    }
-                } catch (InterruptedException ignored) {
-                    // 忽略中断
-                }
-            });
-
-            watchdogThread.setDaemon(true);
-            watchdogThread.start();
-
-            try {
-                result = this.context.eval(source);
-            } finally {
-                watchdogThread.interrupt(); // 确保看门狗线程停止
-            }
-
-            // 转换结果为 Java 对象
-            return convertResultToJava(result);
-        } catch (PolyglotException e) {
-            handleScriptException(e, script);
-            throw e;
-        }
-    }
-
-    /**
-     * 绑定上下文变量
-     *
-     * @param context 脚本上下文
-     */
-    private void bindContextVariables(ScriptContext context) {
-        Value bindings = this.context.getBindings(LANGUAGE_ID);
-
-        // 添加上下文参数
-        if (MapUtil.isNotEmpty(context.getParameters())) {
-            context.getParameters().forEach(bindings::putMember);
-        }
-
-        // 添加上下文函数
-        if (MapUtil.isNotEmpty(context.getFunctions())) {
-            context.getFunctions().forEach(bindings::putMember);
-        }
-
-        // 添加工具类
-        bindings.putMember("utils", ScriptUtils.getInstance());
-
-        // 添加日志对象
-        bindings.putMember("log", log);
-
-        // 添加控制台输出(限制并重定向到日志)
-        AtomicReference<StringBuilder> consoleBuffer = new AtomicReference<>(new StringBuilder());
-
-        Value console = this.context.eval(LANGUAGE_ID, "({\n" +
-                "  log: function(msg) { _consoleLog(msg, 'INFO'); },\n" +
-                "  info: function(msg) { _consoleLog(msg, 'INFO'); },\n" +
-                "  warn: function(msg) { _consoleLog(msg, 'WARN'); },\n" +
-                "  error: function(msg) { _consoleLog(msg, 'ERROR'); }\n" +
-                "})");
-
-        bindings.putMember("console", console);
-
-        bindings.putMember("_consoleLog", (java.util.function.BiConsumer<String, String>) (message, level) -> {
-            String formattedMsg = String.valueOf(message);
-            switch (level) {
-                case "INFO":
-                    log.info("Script console: {}", formattedMsg);
-                    break;
-                case "WARN":
-                    log.warn("Script console: {}", formattedMsg);
-                    break;
-                case "ERROR":
-                    log.error("Script console: {}", formattedMsg);
-                    break;
-                default:
-                    log.info("Script console: {}", formattedMsg);
-            }
-
-            // 将输出添加到缓冲区
-            StringBuilder buffer = consoleBuffer.get();
-            if (buffer.length() > 10000) {
-                buffer = new StringBuilder();
-                consoleBuffer.set(buffer);
-            }
-            buffer.append(formattedMsg).append("\n");
-        });
-    }
-
-    /**
-     * 从缓存中获取或创建脚本源
-     *
-     * @param script 脚本内容
-     * @return 脚本源
-     */
-    private Source getOrCreateSource(String script) {
-        // 如果缓存太大,清理部分缓存
-        if (sourceCache.size() > MAX_CACHE_SIZE) {
-            int itemsToRemove = (int) (MAX_CACHE_SIZE * 0.2); // 清理 20% 的缓存
-            sourceCache.keySet().stream()
-                    .limit(itemsToRemove)
-                    .toList()
-                    .forEach(sourceCache::remove);
-        }
-
-        // 使用脚本的哈希码作为缓存键
-        String cacheKey = String.valueOf(script.hashCode());
-
-        return sourceCache.computeIfAbsent(cacheKey, key -> {
-            try {
-                return Source.newBuilder(LANGUAGE_ID, script, "script-" + key + ".js").cached(true).build();
-            } catch (Exception e) {
-                log.error("创建脚本源失败: {}", e.getMessage(), e);
-                throw new RuntimeException("创建脚本源失败: " + e.getMessage(), e);
-            }
-        });
-    }
-
-    /**
-     * 将 GraalJS 结果转换为 Java 对象
-     *
-     * @param result GraalJS 执行结果
-     * @return Java 对象
-     */
-    private Object convertResultToJava(Value result) {
-        if (result == null || result.isNull()) {
-            return null;
-        }
-
-        if (result.isString()) {
-            return result.asString();
-        }
-
-        if (result.isNumber()) {
-            if (result.fitsInInt()) {
-                return result.asInt();
-            }
-            if (result.fitsInLong()) {
-                return result.asLong();
-            }
-            if (result.fitsInFloat()) {
-                return result.asFloat();
-            }
-            if (result.fitsInDouble()) {
-                return result.asDouble();
-            }
-        }
-
-        if (result.isBoolean()) {
-            return result.asBoolean();
-        }
-
-        if (result.hasArrayElements()) {
-            int size = (int) result.getArraySize();
-            Object[] array = new Object[size];
-            for (int i = 0; i < size; i++) {
-                array[i] = convertResultToJava(result.getArrayElement(i));
-            }
-            return array;
-        }
-
-        if (result.hasMembers()) {
-            Map<String, Object> map = MapUtil.newHashMap();
-            for (String key : result.getMemberKeys()) {
-                map.put(key, convertResultToJava(result.getMember(key)));
-            }
-            return map;
-        }
-
-        if (result.isHostObject()) {
-            return result.asHostObject();
-        }
-
-        // 默认情况下尝试转换为字符串
-        return result.toString();
-    }
-
-    /**
-     * 处理脚本执行异常
-     *
-     * @param e      多语言异常
-     * @param script 原始脚本
-     */
-    private void handleScriptException(PolyglotException e, String script) {
-        if (e.isCancelled()) {
-            log.error("脚本执行被取消,可能超出资源限制");
-        } else if (e.isHostException()) {
-            Throwable hostException = e.asHostException();
-            log.error("脚本执行时发生 Java 异常: {}", hostException.getMessage(), hostException);
-        } else if (e.isGuestException()) {
-            if (e.getSourceLocation() != null) {
-                log.error("脚本执行错误: {} 位于行 {},列 {}",
-                        e.getMessage(),
-                        e.getSourceLocation().getStartLine(),
-                        e.getSourceLocation().getStartColumn());
-
-                // 尝试显示错误位置上下文
-                try {
-                    String[] lines = script.split("\n");
-                    int lineNumber = e.getSourceLocation().getStartLine();
-                    if (lineNumber > 0 && lineNumber <= lines.length) {
-                        int contextStart = Math.max(1, lineNumber - 2);
-                        int contextEnd = Math.min(lines.length, lineNumber + 2);
-
-                        StringBuilder context = new StringBuilder();
-                        for (int i = contextStart; i <= contextEnd; i++) {
-                            if (i == lineNumber) {
-                                context.append("> "); // 标记错误行
-                            } else {
-                                context.append("  ");
-                            }
-                            context.append(i).append(": ").append(lines[i - 1]).append("\n");
-                        }
-                        log.error("脚本上下文:\n{}", context);
-                    }
-                } catch (Exception ignored) {
-                    // 忽略上下文显示失败
-                }
-            } else {
-                log.error("脚本执行错误: {}", e.getMessage());
-            }
-        } else {
-            log.error("脚本执行时发生未知错误: {}", e.getMessage(), e);
-        }
-    }
-
-    @Override
-    public String getType() {
-        return TYPE;
-    }
-
-    @Override
-    public void close() {
-        try {
-            // 清除脚本缓存
-            sourceCache.clear();
-
-            // 关闭 GraalJS 上下文,释放资源
-            context.close(true);
-        } catch (Exception e) {
-            log.warn("关闭 GraalJS 引擎时发生错误: {}", e.getMessage());
-        }
-    }
-}

+ 0 - 25
yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/script/engine/ScriptEngine.java

@@ -1,25 +0,0 @@
-package cn.iocoder.yudao.module.iot.script.engine;
-
-import cn.iocoder.yudao.module.iot.script.context.ScriptContext;
-
-/**
- * 脚本引擎接口,定义脚本执行的核心功能
- */
-public interface ScriptEngine {
-
-    /**
-     * 执行脚本
-     *
-     * @param script  脚本内容
-     * @param context 脚本上下文
-     * @return 脚本执行结果
-     */
-    Object execute(String script, ScriptContext context);
-
-    /**
-     * 获取脚本引擎类型
-     *
-     * @return 脚本引擎类型
-     */
-    String getType();
-}

+ 0 - 85
yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/script/engine/ScriptEngineFactory.java

@@ -1,85 +0,0 @@
-package cn.iocoder.yudao.module.iot.script.engine;
-
-import cn.iocoder.yudao.module.iot.script.sandbox.JsSandbox;
-import lombok.extern.slf4j.Slf4j;
-import org.springframework.beans.factory.DisposableBean;
-import org.springframework.stereotype.Component;
-
-import java.util.Map;
-import java.util.concurrent.ConcurrentHashMap;
-
-/**
- * 脚本引擎工厂,用于创建和缓存不同类型的脚本引擎,支持资源生命周期管理
- */
-@Slf4j
-@Component
-public class ScriptEngineFactory implements DisposableBean {
-
-    /**
-     * 脚本引擎缓存
-     */
-    private final Map<String, ScriptEngine> engines = new ConcurrentHashMap<>();
-
-    /**
-     * 获取脚本引擎
-     *
-     * @param type 脚本类型
-     * @return 脚本引擎
-     */
-    public ScriptEngine getEngine(String type) {
-        // 从缓存中获取引擎
-        return engines.computeIfAbsent(type, this::createEngine);
-    }
-
-    /**
-     * 创建脚本引擎
-     *
-     * @param type 脚本类型
-     * @return 脚本引擎
-     */
-    private ScriptEngine createEngine(String type) {
-        try {
-            if (JsScriptEngine.TYPE.equals(type)) {
-                log.info("创建 GraalJS 脚本引擎");
-                return new JsScriptEngine(new JsSandbox());
-            }
-
-            log.warn("不支持的脚本类型: {}", type);
-            return null;
-        } catch (Exception e) {
-            log.error("创建脚本引擎 [{}] 失败: {}", type, e.getMessage(), e);
-            throw new RuntimeException("创建脚本引擎失败: " + e.getMessage(), e);
-        }
-    }
-
-    /**
-     * 释放指定类型的引擎资源
-     *
-     * @param type 脚本类型
-     */
-    public void releaseEngine(String type) {
-        ScriptEngine engine = engines.remove(type);
-        if (engine instanceof AutoCloseable) {
-            try {
-                ((AutoCloseable) engine).close();
-                log.info("已释放脚本引擎资源: {}", type);
-            } catch (Exception e) {
-                log.warn("释放脚本引擎 [{}] 资源时发生错误: {}", type, e.getMessage());
-            }
-        }
-    }
-
-    /**
-     * 清理所有引擎资源
-     */
-    public void releaseAllEngines() {
-        engines.keySet().forEach(this::releaseEngine);
-        log.info("已清理所有脚本引擎资源");
-    }
-
-    @Override
-    public void destroy() {
-        log.info("应用关闭,释放所有脚本引擎资源...");
-        releaseAllEngines();
-    }
-}

+ 0 - 209
yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/script/example/GraalJsExample.java

@@ -1,209 +0,0 @@
-package cn.iocoder.yudao.module.iot.script.example;
-
-import cn.hutool.core.map.MapUtil;
-import cn.iocoder.yudao.module.iot.script.context.DefaultScriptContext;
-import cn.iocoder.yudao.module.iot.script.context.ScriptContext;
-import cn.iocoder.yudao.module.iot.script.service.ScriptService;
-import lombok.extern.slf4j.Slf4j;
-import org.springframework.beans.factory.annotation.Autowired;
-import org.springframework.stereotype.Component;
-
-import java.util.Map;
-import java.util.concurrent.CompletableFuture;
-import java.util.concurrent.ExecutionException;
-import java.util.concurrent.TimeUnit;
-import java.util.concurrent.TimeoutException;
-
-// TODO @haohao:搞到 test 里面哈;
-/**
- * GraalJS 脚本引擎示例
- * <p>
- * 展示了如何使用 GraalJS 脚本引擎的各种功能
- */
-@Slf4j
-@Component
-public class GraalJsExample {
-
-    @Autowired
-    private ScriptService scriptService;
-
-    /**
-     * 执行简单的 JavaScript 脚本
-     *
-     * @return 执行结果
-     */
-    public Object executeSimpleScript() {
-        // 简单的脚本内容
-        String script = "var result = a + b; result;";
-
-        // 创建参数
-        Map<String, Object> params = MapUtil.newHashMap();
-        params.put("a", 10);
-        params.put("b", 20);
-
-        // 执行脚本
-        return scriptService.executeJavaScript(script, params);
-    }
-
-    /**
-     * 执行现代 JavaScript 语法(ES6+)
-     *
-     * @return 执行结果
-     */
-    public Object executeModernJavaScript() {
-        // 使用现代 JavaScript 语法
-        String script = "// 使用箭头函数\n" +
-                "const add = (a, b) => a + b;\n" +
-                "\n" +
-                "// 使用解构赋值\n" +
-                "const {c, d} = params;\n" +
-                "\n" +
-                "// 使用模板字符串\n" +
-                "const result = `计算结果: ${add(c, d)}`;\n" +
-                "\n" +
-                "// 使用可选链操作符\n" +
-                "const value = params?.e?.value ?? 'default';\n" +
-                "\n" +
-                "// 返回一个对象\n" +
-                "({\n" +
-                "    sum: add(c, d),\n" +
-                "    message: result,\n" +
-                "    defaultValue: value\n" +
-                "})";
-
-        // 创建参数
-        Map<String, Object> params = MapUtil.newHashMap();
-        params.put("params", MapUtil.builder()
-                .put("c", 30)
-                .put("d", 40)
-                .build());
-
-        // 执行脚本
-        return scriptService.executeJavaScript(script, params);
-    }
-
-    /**
-     * 执行带错误处理的脚本
-     *
-     * @return 执行结果
-     */
-    public Object executeWithErrorHandling() {
-        // 包含错误处理的脚本
-        String script = "try {\n" +
-                "    // 故意制造错误\n" +
-                "    if (!nonExistentVar) {\n" +
-                "        throw new Error('手动抛出的错误');\n" +
-                "    }\n" +
-                "} catch (error) {\n" +
-                "    console.error('捕获到错误: ' + error.message);\n" +
-                "    return { success: false, error: error.message };\n" +
-                "}\n" +
-                "\n" +
-                "return { success: true, data: 'No error' };";
-
-        // 执行脚本
-        return scriptService.executeJavaScript(script, MapUtil.newHashMap());
-    }
-
-    /**
-     * 演示超时控制
-     *
-     * @return 执行结果
-     */
-    public Object executeWithTimeout() {
-        // 这个脚本会导致无限循环
-        String script = "// 无限循环\n" +
-                "var counter = 0;\n" +
-                "while(true) {\n" +
-                "    counter++;\n" +
-                "    if (counter % 1000000 === 0) {\n" +
-                "        console.log('Still running: ' + counter);\n" +
-                "    }\n" +
-                "}\n" +
-                "return counter;";
-
-        // 使用 CompletableFuture 和超时控制
-        CompletableFuture<Object> future = CompletableFuture.supplyAsync(() -> {
-            try {
-                return scriptService.executeJavaScript(script, MapUtil.newHashMap());
-            } catch (Exception e) {
-                log.error("脚本执行失败: {}", e.getMessage());
-                return "执行失败: " + e.getMessage();
-            }
-        });
-
-        try {
-            // 等待结果,最多 10 秒
-            return future.get(10, TimeUnit.SECONDS);
-        } catch (InterruptedException | ExecutionException e) {
-            return "执行异常: " + e.getMessage();
-        } catch (TimeoutException e) {
-            future.cancel(true);
-            return "执行超时,已强制终止";
-        }
-    }
-
-    /**
-     * 演示 JSON 处理
-     *
-     * @return 执行结果
-     */
-    public Object executeJsonProcessing() {
-        // JSON 处理示例
-        String script = "// 解析传入的 JSON\n" +
-                "var data = JSON.parse(jsonString);\n" +
-                "\n" +
-                "// 处理 JSON 数据\n" +
-                "var result = {\n" +
-                "    name: data.name.toUpperCase(),\n" +
-                "    age: data.age + 1,\n" +
-                "    address: data.address || 'Unknown',\n" +
-                "    tags: [...data.tags, 'processed'],\n" +
-                "    timestamp: Date.now()\n" +
-                "};\n" +
-                "\n" +
-                "// 转换回 JSON 字符串\n" +
-                "JSON.stringify(result);";
-
-        // 创建上下文
-        ScriptContext context = new DefaultScriptContext();
-        context.setParameter("jsonString",
-                "{\"name\":\"test user\",\"age\":25,\"tags\":[\"tag1\",\"tag2\"]}");
-
-        // 执行脚本
-        return scriptService.executeJavaScript(script, context);
-    }
-
-    /**
-     * 演示数据转换
-     *
-     * @return 执行结果
-     */
-    public Object executeDataConversion() {
-        // 数据转换和处理示例
-        String script = "// 使用 utils 工具类进行数据转换\n" +
-                "var stringValue = utils.isEmpty(input) ? \"默认值\" : input;\n" +
-                "var numberValue = utils.convert(stringValue, \"number\");\n" +
-                "\n" +
-                "// 创建一个复杂数据结构\n" +
-                "var result = {\n" +
-                "    original: input,\n" +
-                "    stringValue: stringValue,\n" +
-                "    numberValue: numberValue,\n" +
-                "    booleanValue: Boolean(numberValue),\n" +
-                "    isValid: utils.isNotEmpty(input)\n" +
-                "};\n" +
-                "\n" +
-                "// 记录处理结果\n" +
-                "log.info(\"处理结果: \" + utils.toJson(result));\n" +
-                "\n" +
-                "return result;";
-
-        // 创建参数
-        Map<String, Object> params = MapUtil.newHashMap();
-        params.put("input", "42");
-
-        // 执行脚本
-        return scriptService.executeJavaScript(script, params);
-    }
-}

+ 0 - 174
yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/script/example/ProductScriptSamples.java

@@ -1,174 +0,0 @@
-package cn.iocoder.yudao.module.iot.script.example;
-
-import lombok.extern.slf4j.Slf4j;
-import org.springframework.stereotype.Component;
-
-/**
- * 产品脚本示例类,提供各种类型的产品脚本示例代码
- */
-@Slf4j
-@Component
-public class ProductScriptSamples {
-
-    /**
-     * 获取属性解析脚本示例
-     *
-     * @return 属性解析脚本示例代码
-     */
-    public String getPropertyParserSample() {
-        return "/**\n" +
-                " * 属性上报数据解析脚本示例\n" +
-                " * @param input 设备上报的原始数据\n" +
-                " * @param productKey 产品标识\n" +
-                " * @param method 方法类型,固定为 property\n" +
-                " * @param properties 当前设备的属性数据\n" +
-                " * @return 解析后的属性数据\n" +
-                " */\n" +
-                "function parseProperty(input, productKey) {\n" +
-                "    // 记录日志\n" +
-                "    console.log('开始解析属性数据: ' + input);\n" +
-                "    \n" +
-                "    try {\n" +
-                "        // 假设上报的是 JSON 字符串\n" +
-                "        var data = JSON.parse(input);\n" +
-                "        \n" +
-                "        // 构建属性数据结构\n" +
-                "        var result = {\n" +
-                "            // 属性上报的时间戳,毫秒级\n" +
-                "            timestamp: data.timestamp || Date.now(),\n" +
-                "            // 属性数据\n" +
-                "            params: {}\n" +
-                "        };\n" +
-                "        \n" +
-                "        // 处理属性值\n" +
-                "        if (data.temperature) {\n" +
-                "            result.params.temperature = parseFloat(data.temperature);\n" +
-                "        }\n" +
-                "        \n" +
-                "        if (data.humidity) {\n" +
-                "            result.params.humidity = parseFloat(data.humidity);\n" +
-                "        }\n" +
-                "        \n" +
-                "        console.log('属性解析结果: ' + JSON.stringify(result));\n" +
-                "        return result;\n" +
-                "    } catch (error) {\n" +
-                "        console.error('解析属性数据失败: ' + error.message);\n" +
-                "        throw new Error('解析失败: ' + error.message);\n" +
-                "    }\n" +
-                "};\n" +
-                "\n" +
-                "// 执行解析\n" +
-                "parseProperty(input, productKey);";
-    }
-
-    /**
-     * 获取事件解析脚本示例
-     *
-     * @return 事件解析脚本示例代码
-     */
-    public String getEventParserSample() {
-        return "/**\n" +
-                " * 事件数据解析脚本示例\n" +
-                " * @param input 设备上报的原始数据\n" +
-                " * @param productKey 产品标识\n" +
-                " * @param method 方法类型,固定为 event\n" +
-                " * @param identifier 事件标识符\n" +
-                " * @return 解析后的事件数据\n" +
-                " */\n" +
-                "function parseEvent(input, productKey, identifier) {\n" +
-                "    // 记录日志\n" +
-                "    console.log('开始解析事件数据: ' + input);\n" +
-                "    console.log('事件标识符: ' + identifier);\n" +
-                "    \n" +
-                "    try {\n" +
-                "        // 假设上报的是 JSON 字符串\n" +
-                "        var data = JSON.parse(input);\n" +
-                "        \n" +
-                "        // 构建事件数据结构\n" +
-                "        var result = {\n" +
-                "            // 事件标识符\n" +
-                "            identifier: identifier || 'alert',\n" +
-                "            // 事件上报的时间戳,毫秒级\n" +
-                "            timestamp: data.timestamp || Date.now(),\n" +
-                "            // 事件参数\n" +
-                "            params: {}\n" +
-                "        };\n" +
-                "        \n" +
-                "        // 根据不同事件类型处理参数\n" +
-                "        if (result.identifier === 'alert') {\n" +
-                "            result.params.level = data.level || 'info';\n" +
-                "            result.params.message = data.message || '';\n" +
-                "        } else if (result.identifier === 'error') {\n" +
-                "            result.params.code = data.code || 0;\n" +
-                "            result.params.message = data.message || '';\n" +
-                "        }\n" +
-                "        \n" +
-                "        console.log('事件解析结果: ' + JSON.stringify(result));\n" +
-                "        return result;\n" +
-                "    } catch (error) {\n" +
-                "        console.error('解析事件数据失败: ' + error.message);\n" +
-                "        throw new Error('解析失败: ' + error.message);\n" +
-                "    }\n" +
-                "};\n" +
-                "\n" +
-                "// 执行解析\n" +
-                "parseEvent(input, productKey, identifier);";
-    }
-
-    /**
-     * 获取命令编码脚本示例
-     *
-     * @return 命令编码脚本示例代码
-     */
-    public String getCommandEncoderSample() {
-        return "/**\n" +
-                " * 命令数据编码脚本示例\n" +
-                " * @param input 平台下发的命令数据\n" +
-                " * @param productKey 产品标识\n" +
-                " * @param method 方法类型,固定为 command\n" +
-                " * @param cmdParams 命令参数\n" +
-                " * @return 编码后的命令数据\n" +
-                " */\n" +
-                "function encodeCommand(input, productKey, cmdParams) {\n" +
-                "    // 记录日志\n" +
-                "    console.log('开始编码命令数据: ' + input);\n" +
-                "    console.log('命令参数: ' + JSON.stringify(cmdParams));\n" +
-                "    \n" +
-                "    try {\n" +
-                "        // 输入可能是 JSON 字符串或对象\n" +
-                "        var data = typeof input === 'string' ? JSON.parse(input) : input;\n" +
-                "        \n" +
-                "        // 获取命令名称和值\n" +
-                "        var cmdName = cmdParams.cmdName || '';\n" +
-                "        var cmdValue = cmdParams.cmdValue;\n" +
-                "        \n" +
-                "        // 构建设备可识别的命令格式\n" +
-                "        var result = {\n" +
-                "            cmd: cmdName,\n" +
-                "            value: cmdValue,\n" +
-                "            timestamp: Date.now()\n" +
-                "        };\n" +
-                "        \n" +
-                "        // 根据不同命令类型构建参数\n" +
-                "        if (cmdName === 'setValue') {\n" +
-                "            // 无需额外处理\n" +
-                "        } else if (cmdName === 'control') {\n" +
-                "            result.mode = data.mode || 'auto';\n" +
-                "            result.action = data.action || 'start';\n" +
-                "        }\n" +
-                "        \n" +
-                "        // 转换为设备能识别的格式(此处以 JSON 字符串为例)\n" +
-                "        var encodedResult = JSON.stringify(result);\n" +
-                "        \n" +
-                "        console.log('命令编码结果: ' + encodedResult);\n" +
-                "        return encodedResult;\n" +
-                "    } catch (error) {\n" +
-                "        console.error('编码命令数据失败: ' + error.message);\n" +
-                "        throw new Error('编码失败: ' + error.message);\n" +
-                "    }\n" +
-                "};\n" +
-                "\n" +
-                "// 执行编码\n" +
-                "encodeCommand(input, productKey, cmdParams);";
-    }
-}

+ 0 - 4
yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/script/package-info.java

@@ -1,4 +0,0 @@
-/**
- * IoT 脚本模块,提供脚本引擎、执行环境和沙箱功能,支持 JavaScript 脚本的执行
- */
-package cn.iocoder.yudao.module.iot.script;

+ 0 - 330
yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/script/sandbox/JsSandbox.java

@@ -1,330 +0,0 @@
-package cn.iocoder.yudao.module.iot.script.sandbox;
-
-import cn.hutool.core.collection.CollUtil;
-import cn.hutool.core.util.StrUtil;
-import lombok.extern.slf4j.Slf4j;
-
-import java.util.ArrayList;
-import java.util.Arrays;
-import java.util.List;
-import java.util.regex.Matcher;
-import java.util.regex.Pattern;
-
-/**
- * JavaScript 沙箱实现,提供脚本安全性验证
- */
-@Slf4j
-public class JsSandbox implements ScriptSandbox {
-
-    /**
-     * JavaScript 沙箱类型
-     */
-    public static final String TYPE = "js";
-
-    /**
-     * 不安全的关键字列表
-     */
-    private final List<String> unsafeKeywords = new ArrayList<>();
-
-    /**
-     * 可能导致高资源消耗的关键字
-     */
-    private final List<String> highResourceKeywords = new ArrayList<>();
-
-    /**
-     * 不安全的包/类访问模式
-     */
-    private final List<Pattern> unsafePatterns = new ArrayList<>();
-
-    /**
-     * 递归或循环嵌套深度检测模式
-     */
-    private final List<Pattern> recursionPatterns = new ArrayList<>();
-
-    /**
-     * 允许的脚本最大长度(字节)
-     */
-    private static final int MAX_SCRIPT_LENGTH = 100 * 1024; // 100KB
-
-    /**
-     * 脚本安全验证超时时间(毫秒)
-     */
-    private static final long VALIDATION_TIMEOUT = 1000; // 1秒
-
-    /**
-     * 构造函数,初始化不安全的关键字和模式
-     */
-    public JsSandbox() {
-        // 初始化 Java 相关的不安全关键字
-        // TODO @haohao:可以使用 addAll 哈。
-        Arrays.asList(
-                "java.lang.System",
-                "java.io",
-                "java.net",
-                "java.nio",
-                "java.security",
-                "java.rmi",
-                "java.lang.reflect",
-                "java.sql",
-                "javax.sql",
-                "javax.naming",
-                "javax.script",
-                "javax.tools",
-                "org.omg",
-                "org.graalvm.polyglot",
-                "sun.",
-                "javafx.",
-                "Packages.",
-                "com.sun.",
-                "com.oracle.").forEach(unsafeKeywords::add);
-
-        // GraalJS 特有的不安全关键字
-        Arrays.asList(
-                "Polyglot.import",
-                "Polyglot.eval",
-                "Java.type",
-                "allowHostAccess",
-                "allowNativeAccess",
-                "allowIO",
-                "allowHostClassLoading",
-                "allowAllAccess",
-                "allowExperimentalOptions",
-                "Context.Builder",
-                "Context.create",
-                "Context.getCurrent",
-                "Context.newBuilder",
-                "__proto__",
-                "__defineGetter__",
-                "__defineSetter__",
-                "__lookupGetter__",
-                "__lookupSetter__",
-                "__noSuchMethod__",
-                "constructor.constructor",
-                "Object.constructor").forEach(unsafeKeywords::add);
-
-        // 可能导致高资源消耗的关键字
-        Arrays.asList(
-                "while(true)",
-                "for(;;)",
-                "do{",
-                "BigInt",
-                "Promise.all",
-                "setTimeout",
-                "setInterval",
-                "new Array(",
-                "Array(",
-                "new ArrayBuffer(",
-                ".repeat(",
-                ".forEach(",
-                ".map(",
-                ".reduce(").forEach(highResourceKeywords::add);
-
-        // 初始化不安全的模式
-        // 系统访问和进程执行
-        unsafePatterns.add(Pattern.compile("java\\.lang\\.Runtime"));
-        unsafePatterns.add(Pattern.compile("java\\.lang\\.ProcessBuilder"));
-        unsafePatterns.add(Pattern.compile("java\\.lang\\.reflect"));
-
-        // 特殊对象和操作
-        unsafePatterns.add(Pattern.compile("Packages"));
-        unsafePatterns.add(Pattern.compile("JavaImporter"));
-        unsafePatterns.add(Pattern.compile("load\\s*\\("));
-        unsafePatterns.add(Pattern.compile("loadWithNewGlobal\\s*\\("));
-        unsafePatterns.add(Pattern.compile("exit\\s*\\("));
-        unsafePatterns.add(Pattern.compile("quit\\s*\\("));
-        unsafePatterns.add(Pattern.compile("eval\\s*\\("));
-
-        // GraalJS 特有的不安全模式
-        unsafePatterns.add(Pattern.compile("Polyglot\\."));
-        unsafePatterns.add(Pattern.compile("Java\\.type\\s*\\("));
-        unsafePatterns.add(Pattern.compile("Context\\."));
-        unsafePatterns.add(Pattern.compile("Engine\\."));
-
-        // 原型污染检测
-        unsafePatterns.add(Pattern.compile("(?:Object|Array|String|Number|Boolean|Function|RegExp|Date)\\.prototype"));
-        unsafePatterns.add(Pattern.compile("\\['constructor'\\]"));
-        unsafePatterns.add(Pattern.compile("\\[\"constructor\"\\]"));
-        unsafePatterns.add(Pattern.compile("\\['__proto__'\\]"));
-        unsafePatterns.add(Pattern.compile("\\[\"__proto__\"\\]"));
-
-        // 检测可能导致无限递归或循环的模式
-        recursionPatterns.add(Pattern.compile("for\\s*\\([^\\)]*\\)\\s*\\{[^\\}]*for\\s*\\(")); // 嵌套循环
-        recursionPatterns.add(Pattern.compile("while\\s*\\([^\\)]*\\)\\s*\\{[^\\}]*while\\s*\\(")); // 嵌套 while
-        recursionPatterns.add(Pattern.compile("function\\s+[a-zA-Z0-9_$]+\\s*\\([^\\)]*\\)\\s*\\{[^\\}]*\\1\\s*\\(")); // 递归函数调用
-    }
-
-    @Override
-    public boolean validate(String script) {
-        if (StrUtil.isBlank(script)) {
-            return true;
-        }
-
-        // 检查脚本长度
-        if (script.length() > MAX_SCRIPT_LENGTH) {
-            log.warn("脚本长度超过限制: {} > {}", script.length(), MAX_SCRIPT_LENGTH);
-            return false;
-        }
-
-        // 使用超时机制进行验证
-        final boolean[] result = {true};
-        Thread validationThread = new Thread(() -> {
-            // 检查不安全的关键字
-            if (containsUnsafeKeywords(script)) {
-                result[0] = false;
-                return;
-            }
-
-            // 检查不安全的模式
-            if (matchesUnsafePatterns(script)) {
-                result[0] = false;
-                return;
-            }
-
-            // 检查可能导致高资源消耗的构造
-            if (containsHighResourcePatterns(script)) {
-                log.warn("脚本包含可能导致高资源消耗的构造,需要注意");
-                // 不直接拒绝,而是记录警告
-            }
-
-            // 分析脚本复杂度
-            analyzeScriptComplexity(script);
-        });
-
-        validationThread.start();
-        try {
-            validationThread.join(VALIDATION_TIMEOUT);
-            if (validationThread.isAlive()) {
-                validationThread.interrupt();
-                log.warn("脚本安全验证超时");
-                return false;
-            }
-        } catch (InterruptedException e) {
-            log.warn("脚本安全验证被中断");
-            return false;
-        }
-
-        return result[0];
-    }
-
-    @Override
-    public String getType() {
-        return TYPE;
-    }
-
-    /**
-     * 检查脚本是否包含不安全的关键字
-     *
-     * @param script 脚本内容
-     * @return 是否包含不安全的关键字
-     */
-    private boolean containsUnsafeKeywords(String script) {
-        if (CollUtil.isEmpty(unsafeKeywords)) {
-            return false;
-        }
-
-        for (String keyword : unsafeKeywords) {
-            if (script.contains(keyword)) {
-                log.warn("脚本包含不安全的关键字: {}", keyword);
-                return true;
-            }
-        }
-        return false;
-    }
-
-    /**
-     * 检查脚本是否匹配不安全的模式
-     *
-     * @param script 脚本内容
-     * @return 是否匹配不安全的模式
-     */
-    private boolean matchesUnsafePatterns(String script) {
-        if (CollUtil.isEmpty(unsafePatterns)) {
-            return false;
-        }
-
-        for (Pattern pattern : unsafePatterns) {
-            Matcher matcher = pattern.matcher(script);
-            if (matcher.find()) {
-                log.warn("脚本匹配到不安全的模式: {}", pattern.pattern());
-                return true;
-            }
-        }
-        return false;
-    }
-
-    /**
-     * 检查脚本是否包含可能导致高资源消耗的模式
-     *
-     * @param script 脚本内容
-     * @return 是否包含高资源消耗模式
-     */
-    private boolean containsHighResourcePatterns(String script) {
-        if (CollUtil.isEmpty(highResourceKeywords)) {
-            return false;
-        }
-
-        boolean result = false;
-        for (String pattern : highResourceKeywords) {
-            if (script.contains(pattern)) {
-                log.warn("脚本包含高资源消耗模式: {}", pattern);
-                result = true;
-            }
-        }
-
-        // 还要检查递归或嵌套循环模式
-        for (Pattern pattern : recursionPatterns) {
-            Matcher matcher = pattern.matcher(script);
-            if (matcher.find()) {
-                log.warn("脚本包含嵌套循环或递归调用: {}", pattern.pattern());
-                result = true;
-            }
-        }
-
-        return result;
-    }
-
-    /**
-     * 分析脚本复杂度
-     *
-     * @param script 脚本内容
-     */
-    private void analyzeScriptComplexity(String script) {
-        // 计算循环和条件语句的数量
-        int forCount = countOccurrences(script, "for(");
-        forCount += countOccurrences(script, "for (");
-
-        int whileCount = countOccurrences(script, "while(");
-        whileCount += countOccurrences(script, "while (");
-
-        int doWhileCount = countOccurrences(script, "do{");
-        doWhileCount += countOccurrences(script, "do {");
-
-        int funcCount = countOccurrences(script, "function");
-
-        // 记录复杂度评估
-        if (forCount + whileCount + doWhileCount > 10) {
-            log.warn("脚本循环结构过多: for={}, while={}, do-while={}", forCount, whileCount, doWhileCount);
-        }
-
-        if (funcCount > 20) {
-            log.warn("脚本函数定义过多: {}", funcCount);
-        }
-    }
-
-    /**
-     * 计算字符串出现次数
-     *
-     * @param source    源字符串
-     * @param substring 子字符串
-     * @return 出现次数
-     */
-    private int countOccurrences(String source, String substring) {
-        int count = 0;
-        int index = 0;
-        while ((index = source.indexOf(substring, index)) != -1) {
-            count++;
-            index += substring.length();
-        }
-        return count;
-    }
-}

+ 0 - 22
yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/script/sandbox/ScriptSandbox.java

@@ -1,22 +0,0 @@
-package cn.iocoder.yudao.module.iot.script.sandbox;
-
-/**
- * 脚本沙箱接口,提供脚本安全性验证
- */
-public interface ScriptSandbox {
-
-    /**
-     * 验证脚本内容是否安全
-     *
-     * @param script 脚本内容
-     * @return 脚本是否安全
-     */
-    boolean validate(String script);
-
-    /**
-     * 获取沙箱类型
-     *
-     * @return 沙箱类型
-     */
-    String getType();
-}

+ 0 - 58
yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/script/service/ScriptService.java

@@ -1,58 +0,0 @@
-package cn.iocoder.yudao.module.iot.script.service;
-
-import cn.iocoder.yudao.module.iot.script.context.ScriptContext;
-
-import java.util.Map;
-
-/**
- * 脚本服务接口,定义脚本执行的核心功能
- */
-public interface ScriptService {
-
-    /**
-     * 执行脚本
-     *
-     * @param scriptType 脚本类型(如 js、groovy 等)
-     * @param script     脚本内容
-     * @param context    脚本上下文
-     * @return 脚本执行结果
-     */
-    Object executeScript(String scriptType, String script, ScriptContext context);
-
-    /**
-     * 执行脚本
-     *
-     * @param scriptType 脚本类型(如 js、groovy 等)
-     * @param script     脚本内容
-     * @param params     脚本参数
-     * @return 脚本执行结果
-     */
-    Object executeScript(String scriptType, String script, Map<String, Object> params);
-
-    /**
-     * 执行 JavaScript 脚本
-     *
-     * @param script  脚本内容
-     * @param context 脚本上下文
-     * @return 脚本执行结果
-     */
-    Object executeJavaScript(String script, ScriptContext context);
-
-    /**
-     * 执行 JavaScript 脚本
-     *
-     * @param script 脚本内容
-     * @param params 脚本参数
-     * @return 脚本执行结果
-     */
-    Object executeJavaScript(String script, Map<String, Object> params);
-
-    /**
-     * 验证脚本内容是否安全
-     *
-     * @param scriptType 脚本类型
-     * @param script     脚本内容
-     * @return 脚本是否安全
-     */
-    boolean validateScript(String scriptType, String script);
-}

+ 0 - 111
yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/script/service/ScriptServiceImpl.java

@@ -1,111 +0,0 @@
-package cn.iocoder.yudao.module.iot.script.service;
-
-import cn.hutool.core.map.MapUtil;
-import cn.hutool.core.util.StrUtil;
-import cn.iocoder.yudao.module.iot.script.context.DefaultScriptContext;
-import cn.iocoder.yudao.module.iot.script.context.ScriptContext;
-import cn.iocoder.yudao.module.iot.script.engine.JsScriptEngine;
-import cn.iocoder.yudao.module.iot.script.engine.ScriptEngine;
-import cn.iocoder.yudao.module.iot.script.engine.ScriptEngineFactory;
-import cn.iocoder.yudao.module.iot.script.sandbox.JsSandbox;
-import cn.iocoder.yudao.module.iot.script.sandbox.ScriptSandbox;
-import lombok.extern.slf4j.Slf4j;
-import org.springframework.beans.factory.annotation.Autowired;
-import org.springframework.stereotype.Service;
-
-import java.util.Map;
-
-/**
- * 脚本服务实现类
- */
-@Slf4j
-@Service
-public class ScriptServiceImpl implements ScriptService {
-
-    @Autowired
-    private ScriptEngineFactory engineFactory;
-
-    @Override
-    public Object executeScript(String scriptType, String script, ScriptContext context) {
-        if (StrUtil.isBlank(scriptType) || StrUtil.isBlank(script)) {
-            return null;
-        }
-
-        ScriptEngine engine = engineFactory.getEngine(scriptType);
-        if (engine == null) {
-            log.error("找不到脚本引擎: {}", scriptType);
-            throw new RuntimeException("不支持的脚本类型: " + scriptType);
-        }
-
-        try {
-            return engine.execute(script, context);
-        } catch (Exception e) {
-            // TODO @haohao:最好打印一些参数;下面类似的也是
-            log.error("执行脚本失败: {}", e.getMessage(), e);
-            throw new RuntimeException("执行脚本失败: " + e.getMessage(), e);
-        }
-    }
-
-    @Override
-    public Object executeScript(String scriptType, String script, Map<String, Object> params) {
-        ScriptContext context = createContext(params);
-        return executeScript(scriptType, script, context);
-    }
-
-    @Override
-    public Object executeJavaScript(String script, ScriptContext context) {
-        return executeScript(JsScriptEngine.TYPE, script, context);
-    }
-
-    @Override
-    public Object executeJavaScript(String script, Map<String, Object> params) {
-        return executeScript(JsScriptEngine.TYPE, script, params);
-    }
-
-    @Override
-    public boolean validateScript(String scriptType, String script) {
-        if (StrUtil.isBlank(scriptType) || StrUtil.isBlank(script)) {
-            return true;
-        }
-
-        ScriptSandbox sandbox = getSandbox(scriptType);
-        if (sandbox == null) {
-            log.warn("找不到对应的脚本沙箱: {}", scriptType);
-            return false;
-        }
-
-        try {
-            return sandbox.validate(script);
-        } catch (Exception e) {
-            log.error("验证脚本安全性失败: {}", e.getMessage(), e);
-            return false;
-        }
-    }
-
-    /**
-     * 根据脚本类型获取对应的沙箱实现
-     *
-     * @param scriptType 脚本类型
-     * @return 沙箱实现
-     */
-    private ScriptSandbox getSandbox(String scriptType) {
-        if (JsScriptEngine.TYPE.equals(scriptType)) {
-            return new JsSandbox();
-        }
-        return null;
-    }
-
-    /**
-     * 根据参数创建脚本上下文
-     *
-     * @param params 参数
-     * @return 脚本上下文
-     */
-    private ScriptContext createContext(Map<String, Object> params) {
-        ScriptContext context = new DefaultScriptContext();
-        if (MapUtil.isNotEmpty(params)) {
-            params.forEach(context::setParameter);
-        }
-        return context;
-    }
-}

+ 0 - 159
yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/script/util/ScriptUtils.java

@@ -1,159 +0,0 @@
-package cn.iocoder.yudao.module.iot.script.util;
-
-import cn.hutool.core.convert.Convert;
-import cn.hutool.core.map.MapUtil;
-import cn.hutool.core.util.StrUtil;
-import cn.hutool.json.JSONUtil;
-import lombok.extern.slf4j.Slf4j;
-
-import java.util.Map;
-
-/**
- * 脚本工具类,提供给脚本执行环境使用的工具方法
- */
-@Slf4j
-public class ScriptUtils {
-
-    /**
-     * 单例实例
-     */
-    private static final ScriptUtils INSTANCE = new ScriptUtils();
-
-    /**
-     * 获取单例实例
-     *
-     * @return 工具类实例
-     */
-    public static ScriptUtils getInstance() {
-        return INSTANCE;
-    }
-
-    // TODO @haohao:使用 lombok 简化掉
-    private ScriptUtils() {
-        // 私有构造函数
-    }
-
-    /**
-     * 字符串是否为空
-     *
-     * @param str 字符串
-     * @return 是否为空
-     */
-    public boolean isEmpty(String str) {
-        return StrUtil.isEmpty(str);
-    }
-
-    /**
-     * 字符串是否不为空
-     *
-     * @param str 字符串
-     * @return 是否不为空
-     */
-    public boolean isNotEmpty(String str) {
-        return StrUtil.isNotEmpty(str);
-    }
-
-    /**
-     * 将对象转为 JSON 字符串
-     *
-     * @param obj 对象
-     * @return JSON 字符串
-     */
-    public String toJson(Object obj) {
-        return JSONUtil.toJsonStr(obj);
-    }
-
-    /**
-     * 将 JSON 字符串转为 Map
-     *
-     * @param json JSON 字符串
-     * @return Map 对象
-     */
-    public Map<String, Object> parseJson(String json) {
-        if (StrUtil.isEmpty(json)) {
-            return MapUtil.newHashMap();
-        }
-        try {
-            return JSONUtil.toBean(json, Map.class);
-        } catch (Exception e) {
-            log.error("JSON 解析失败: {}", json, e);
-            return MapUtil.newHashMap();
-        }
-    }
-
-    /**
-     * 类型转换
-     *
-     * @param value 值
-     * @param type  目标类型
-     * @param <T>   泛型
-     * @return 转换后的值
-     */
-    public <T> T convert(Object value, Class<T> type) {
-        return Convert.convert(type, value);
-    }
-
-    /**
-     * 从 Map 中获取值
-     *
-     * @param map Map 对象
-     * @param key 键
-     * @return 值
-     */
-    public Object get(Map<String, Object> map, String key) {
-        return MapUtil.get(map, key, Object.class);
-    }
-
-    /**
-     * 从 Map 中获取字符串
-     *
-     * @param map Map 对象
-     * @param key 键
-     * @return 字符串值
-     */
-    public String getString(Map<String, Object> map, String key) {
-        return MapUtil.getStr(map, key);
-    }
-
-    /**
-     * 从 Map 中获取整数
-     *
-     * @param map Map 对象
-     * @param key 键
-     * @return 整数值
-     */
-    public Integer getInt(Map<String, Object> map, String key) {
-        return MapUtil.getInt(map, key);
-    }
-
-    /**
-     * 从 Map 中获取布尔值
-     *
-     * @param map Map 对象
-     * @param key 键
-     * @return 布尔值
-     */
-    public Boolean getBool(Map<String, Object> map, String key) {
-        return MapUtil.getBool(map, key);
-    }
-
-    /**
-     * 从 Map 中获取双精度浮点数
-     *
-     * @param map Map 对象
-     * @param key 键
-     * @return 双精度浮点数值
-     */
-    public Double getDouble(Map<String, Object> map, String key) {
-        return MapUtil.getDouble(map, key);
-    }
-
-    /**
-     * 获取当前时间戳(毫秒)
-     *
-     * @return 时间戳
-     */
-    public long currentTimeMillis() {
-        return System.currentTimeMillis();
-    }
-}

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

@@ -1,82 +0,0 @@
-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);
-
-}

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

@@ -1,234 +0,0 @@
-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.script.context.DeviceScriptContext;
-import cn.iocoder.yudao.module.iot.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;
-
-// TODO @芋艿:后续再 review 哈!
-/**
- * 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());
-            }
-
-            // 创建测试上下文
-            IotProductDO product = productService.getProduct(testReqVO.getProductId());
-            DeviceScriptContext context = new DeviceScriptContext();
-
-            // 设置设备上下文(使用产品信息,测试时无具体设备)
-            context.withDeviceInfo(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 1: // PROPERTY_PARSER
-                    params.put("method", "property");
-                    // 添加一些模拟的属性数据
-                    Map<String, Object> properties = new HashMap<>();
-                    properties.put("temp", 25.5);
-                    properties.put("humidity", 60);
-                    context.withProperties(properties);
-                    break;
-                case 2: // EVENT_PARSER
-                    params.put("method", "event");
-                    params.put("identifier", "default");
-                    // 添加事件数据
-                    Map<String, Object> eventParams = new HashMap<>();
-                    eventParams.put("timestamp", System.currentTimeMillis());
-                    params.put("eventParams", eventParams);
-                    break;
-                case 3: // COMMAND_ENCODER
-                    params.put("method", "command");
-                    // 添加命令参数
-                    Map<String, Object> cmdParams = new HashMap<>();
-                    cmdParams.put("cmdName", "setValue");
-                    cmdParams.put("cmdValue", 100);
-                    params.put("cmdParams", cmdParams);
-                    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);
-    }
-}

+ 7 - 7
yudao-server/src/main/resources/application-local.yaml

@@ -69,13 +69,13 @@ spring:
           url: jdbc:mysql://127.0.0.1:3306/ruoyi-vue-pro?useSSL=false&serverTimezone=Asia/Shanghai&allowPublicKeyRetrieval=true&rewriteBatchedStatements=true&nullCatalogMeansCurrent=true
           username: root
           password: 123456
-#        tdengine: # IoT 数据库(需要 IoT 物联网再开启噢!)
-#          url: jdbc:TAOS-RS://127.0.0.1:6041/ruoyi_vue_pro
-#          driver-class-name: com.taosdata.jdbc.rs.RestfulDriver
-#          username: root
-#          password: taosdata
-#          druid:
-#            validation-query: SELECT SERVER_STATUS() # TDengine 数据源的有效性检查 SQL
+        tdengine: # IoT 数据库(需要 IoT 物联网再开启噢!)
+          url: jdbc:TAOS-RS://127.0.0.1:6041/ruoyi_vue_pro
+          driver-class-name: com.taosdata.jdbc.rs.RestfulDriver
+          username: root
+          password: taosdata
+          druid:
+            validation-query: SELECT SERVER_STATUS() # TDengine 数据源的有效性检查 SQL
 
   # Redis 配置。Redisson 默认的配置足够使用,一般不需要进行调优
   data: