Răsfoiți Sursa

【功能优化】IoT: 重构脚本服务实现,启用脚本执行和测试逻辑,更新日志记录方式

安浩浩 7 luni în urmă
părinte
comite
0ae893272b

+ 2 - 2
yudao-module-iot/yudao-module-iot-biz/pom.xml

@@ -70,11 +70,11 @@
         </dependency>
 
         <!-- 脚本插件相关 -->
-        <!--<dependency>
+        <dependency>
             <groupId>cn.iocoder.boot</groupId>
             <artifactId>yudao-module-iot-plugin-script</artifactId>
             <version>${revision}</version>
-        </dependency>-->
+        </dependency>
 
         <!-- 消息队列相关 -->
         <dependency>

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

@@ -9,6 +9,8 @@ import cn.iocoder.yudao.module.iot.controller.admin.product.vo.script.IotProduct
 import cn.iocoder.yudao.module.iot.dal.dataobject.product.IotProductDO;
 import cn.iocoder.yudao.module.iot.dal.dataobject.product.IotProductScriptDO;
 import cn.iocoder.yudao.module.iot.dal.mysql.product.IotProductScriptMapper;
+import cn.iocoder.yudao.module.iot.plugin.script.context.PluginScriptContext;
+import cn.iocoder.yudao.module.iot.plugin.script.service.ScriptService;
 import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
 import jakarta.annotation.Resource;
 import lombok.extern.slf4j.Slf4j;
@@ -40,8 +42,8 @@ public class IotProductScriptServiceImpl implements IotProductScriptService {
     @Resource
     private IotProductService productService;
 
-//    @Resource
-//    private ScriptService scriptService;
+    @Resource
+    private ScriptService scriptService;
 
     @Override
     public Long createProductScript(IotProductScriptSaveReqVO createReqVO) {
@@ -118,90 +120,89 @@ public class IotProductScriptServiceImpl implements IotProductScriptService {
 
     @Override
     public IotProductScriptTestRespVO testProductScript(IotProductScriptTestReqVO testReqVO) {
-//        long startTime = System.currentTimeMillis();
-//
-//        try {
-//            // 验证产品是否存在
-//            validateProductExists(testReqVO.getProductId());
-//
-//            // 根据ID获取已保存的脚本(如果有)
-//            IotProductScriptDO existingScript = null;
-//            if (testReqVO.getId() != null) {
-//                existingScript = getProductScript(testReqVO.getId());
-//            }
-//
-//            // 创建测试上下文
-//            PluginScriptContext context = new PluginScriptContext();
-//            IotProductDO product = productService.getProduct(testReqVO.getProductId());
-//
-//            // 设置设备上下文(使用产品信息,没有具体设备)
-//            context.withDeviceContext(product.getProductKey(), null);
-//
-//            // 设置输入参数
-//            Map<String, Object> params = new HashMap<>();
-//            params.put("input", testReqVO.getTestInput());
-//            params.put("productKey", product.getProductKey());
-//            params.put("scriptType", testReqVO.getScriptType());
-//
-//            // 根据脚本类型设置特定参数
-//            switch (testReqVO.getScriptType()) {
-//                case 1: // PROPERTY_PARSER
-//                    params.put("method", "property");
-//                    break;
-//                case 2: // EVENT_PARSER
-//                    params.put("method", "event");
-//                    params.put("identifier", "default");
-//                    break;
-//                case 3: // COMMAND_ENCODER
-//                    params.put("method", "command");
-//                    break;
-//                default:
-//                    // 默认不添加额外参数
-//            }
-//
-//            // 添加所有参数到上下文
-//            for (Map.Entry<String, Object> entry : params.entrySet()) {
-//                context.setParameter(entry.getKey(), entry.getValue());
-//            }
-//
-//            // 执行脚本
-//            Object result = scriptService.executeScript(
-//                    testReqVO.getScriptLanguage(),
-//                    testReqVO.getScriptContent(),
-//                    context);
-//
-//            // 更新测试结果(如果是已保存的脚本)
-//            if (existingScript != null) {
-//                IotProductScriptDO updateObj = new IotProductScriptDO();
-//                updateObj.setId(existingScript.getId());
-//                updateObj.setLastTestTime(LocalDateTime.now());
-//                updateObj.setLastTestResult(1); // 1表示成功
-//                productScriptMapper.updateById(updateObj);
-//            }
-//
-//            long executionTime = System.currentTimeMillis() - startTime;
-//            return IotProductScriptTestRespVO.success(result, executionTime);
-//
-//        } catch (Exception e) {
-//            log.error("[testProductScript][测试脚本异常]", e);
-//
-//            // 如果是已保存的脚本,更新测试失败状态
-//            if (testReqVO.getId() != null) {
-//                try {
-//                    IotProductScriptDO updateObj = new IotProductScriptDO();
-//                    updateObj.setId(testReqVO.getId());
-//                    updateObj.setLastTestTime(LocalDateTime.now());
-//                    updateObj.setLastTestResult(0); // 0表示失败
-//                    productScriptMapper.updateById(updateObj);
-//                } catch (Exception ex) {
-//                    log.error("[testProductScript][更新脚本测试结果异常]", ex);
-//                }
-//            }
-//
-//            long executionTime = System.currentTimeMillis() - startTime;
-//            return IotProductScriptTestRespVO.error(e.getMessage(), executionTime);
-//        }
-        return null;
+        long startTime = System.currentTimeMillis();
+
+        try {
+            // 验证产品是否存在
+            validateProductExists(testReqVO.getProductId());
+
+            // 根据ID获取已保存的脚本(如果有)
+            IotProductScriptDO existingScript = null;
+            if (testReqVO.getId() != null) {
+                existingScript = getProductScript(testReqVO.getId());
+            }
+
+            // 创建测试上下文
+            PluginScriptContext context = new PluginScriptContext();
+            IotProductDO product = productService.getProduct(testReqVO.getProductId());
+
+            // 设置设备上下文(使用产品信息,没有具体设备)
+            context.withDeviceContext(product.getProductKey(), null);
+
+            // 设置输入参数
+            Map<String, Object> params = new HashMap<>();
+            params.put("input", testReqVO.getTestInput());
+            params.put("productKey", product.getProductKey());
+            params.put("scriptType", testReqVO.getScriptType());
+
+            // 根据脚本类型设置特定参数
+            switch (testReqVO.getScriptType()) {
+                case 1: // PROPERTY_PARSER
+                    params.put("method", "property");
+                    break;
+                case 2: // EVENT_PARSER
+                    params.put("method", "event");
+                    params.put("identifier", "default");
+                    break;
+                case 3: // COMMAND_ENCODER
+                    params.put("method", "command");
+                    break;
+                default:
+                    // 默认不添加额外参数
+            }
+
+            // 添加所有参数到上下文
+            for (Map.Entry<String, Object> entry : params.entrySet()) {
+                context.setParameter(entry.getKey(), entry.getValue());
+            }
+
+            // 执行脚本
+            Object result = scriptService.executeScript(
+                    testReqVO.getScriptLanguage(),
+                    testReqVO.getScriptContent(),
+                    context);
+
+            // 更新测试结果(如果是已保存的脚本)
+            if (existingScript != null) {
+                IotProductScriptDO updateObj = new IotProductScriptDO();
+                updateObj.setId(existingScript.getId());
+                updateObj.setLastTestTime(LocalDateTime.now());
+                updateObj.setLastTestResult(1); // 1表示成功
+                productScriptMapper.updateById(updateObj);
+            }
+
+            long executionTime = System.currentTimeMillis() - startTime;
+            return IotProductScriptTestRespVO.success(result, executionTime);
+
+        } catch (Exception e) {
+            log.error("[testProductScript][测试脚本异常]", e);
+
+            // 如果是已保存的脚本,更新测试失败状态
+            if (testReqVO.getId() != null) {
+                try {
+                    IotProductScriptDO updateObj = new IotProductScriptDO();
+                    updateObj.setId(testReqVO.getId());
+                    updateObj.setLastTestTime(LocalDateTime.now());
+                    updateObj.setLastTestResult(0); // 0表示失败
+                    productScriptMapper.updateById(updateObj);
+                } catch (Exception ex) {
+                    log.error("[testProductScript][更新脚本测试结果异常]", ex);
+                }
+            }
+
+            long executionTime = System.currentTimeMillis() - startTime;
+            return IotProductScriptTestRespVO.error(e.getMessage(), executionTime);
+        }
     }
 
     @Override

+ 1 - 1
yudao-module-iot/yudao-module-iot-plugins/yudao-module-iot-plugin-script/pom.xml

@@ -19,7 +19,7 @@
         <!-- 引入公共模块 -->
         <dependency>
             <groupId>cn.iocoder.boot</groupId>
-            <artifactId>yudao-module-iot-plugin-common</artifactId>
+            <artifactId>yudao-module-iot-api</artifactId>
             <version>${revision}</version>
         </dependency>
 

+ 8 - 9
yudao-module-iot/yudao-module-iot-plugins/yudao-module-iot-plugin-script/src/main/java/cn/iocoder/yudao/module/iot/plugin/script/ScriptExample.java

@@ -2,8 +2,7 @@ package cn.iocoder.yudao.module.iot.plugin.script;
 
 import cn.iocoder.yudao.module.iot.plugin.script.context.PluginScriptContext;
 import cn.iocoder.yudao.module.iot.plugin.script.service.ScriptService;
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
+import lombok.extern.slf4j.Slf4j;
 import org.springframework.beans.factory.annotation.Autowired;
 import org.springframework.stereotype.Component;
 
@@ -14,8 +13,8 @@ import java.util.Map;
  * 脚本使用示例类
  */
 @Component
+@Slf4j
 public class ScriptExample {
-    private static final Logger logger = LoggerFactory.getLogger(ScriptExample.class);
 
     @Autowired
     private ScriptService scriptService;
@@ -31,7 +30,7 @@ public class ScriptExample {
         params.put("b", 20);
 
         Object result = scriptService.executeJavaScript(script, params);
-        logger.info("脚本执行结果: {}", result);
+        log.info("脚本执行结果: {}", result);
     }
 
     /**
@@ -73,17 +72,17 @@ public class ScriptExample {
             Object result = scriptService.executeJavaScript(script, context);
             if (result != null) {
                 // 处理结果
-                logger.info("设备数据处理结果: {}", result);
+                log.info("设备数据处理结果: {}", result);
 
                 // 安全地将结果转换为Map
                 if (result instanceof Map) {
                     return (Map<String, Object>) result;
                 } else {
-                    logger.warn("脚本返回结果类型不是Map: {}", result.getClass().getName());
+                    log.warn("脚本返回结果类型不是Map: {}", result.getClass().getName());
                 }
             }
         } catch (Exception e) {
-            logger.error("处理设备数据失败: {}", e.getMessage());
+            log.error("处理设备数据失败: {}", e.getMessage());
         }
 
         return new HashMap<>();
@@ -121,10 +120,10 @@ public class ScriptExample {
             if (result instanceof String) {
                 return (String) result;
             } else if (result != null) {
-                logger.warn("脚本返回结果类型不是String: {}", result.getClass().getName());
+                log.warn("脚本返回结果类型不是String: {}", result.getClass().getName());
             }
         } catch (Exception e) {
-            logger.error("生成设备命令失败: {}", e.getMessage());
+            log.error("生成设备命令失败: {}", e.getMessage());
         }
 
         return null;

+ 10 - 11
yudao-module-iot/yudao-module-iot-plugins/yudao-module-iot-plugin-script/src/main/java/cn/iocoder/yudao/module/iot/plugin/script/engine/JsScriptEngine.java

@@ -4,8 +4,7 @@ import cn.hutool.core.map.MapUtil;
 import cn.iocoder.yudao.module.iot.plugin.script.context.ScriptContext;
 import cn.iocoder.yudao.module.iot.plugin.script.sandbox.JsSandbox;
 import cn.iocoder.yudao.module.iot.plugin.script.util.ScriptUtils;
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
+import lombok.extern.slf4j.Slf4j;
 
 import javax.script.*;
 import java.util.Map;
@@ -16,8 +15,8 @@ import java.util.concurrent.ConcurrentHashMap;
  * JavaScript脚本引擎实现
  * 使用JSR-223 Nashorn脚本引擎
  */
+@Slf4j
 public class JsScriptEngine extends AbstractScriptEngine {
-    private static final Logger logger = LoggerFactory.getLogger(JsScriptEngine.class);
 
     /**
      * 默认脚本执行超时时间(毫秒)
@@ -46,7 +45,7 @@ public class JsScriptEngine extends AbstractScriptEngine {
 
     @Override
     public void init() {
-        logger.info("初始化JavaScript脚本引擎");
+        log.info("初始化JavaScript脚本引擎");
 
         // 创建脚本引擎管理器
         engineManager = new ScriptEngineManager();
@@ -54,7 +53,7 @@ public class JsScriptEngine extends AbstractScriptEngine {
         // 获取JavaScript引擎
         engine = engineManager.getEngineByName(JS_ENGINE_NAME);
         if (engine == null) {
-            logger.error("无法创建JavaScript引擎,尝试使用JavaScript名称获取");
+            log.error("无法创建JavaScript引擎,尝试使用JavaScript名称获取");
             engine = engineManager.getEngineByName("JavaScript");
         }
 
@@ -62,7 +61,7 @@ public class JsScriptEngine extends AbstractScriptEngine {
             throw new IllegalStateException("无法创建JavaScript引擎,请检查环境配置");
         }
 
-        logger.info("成功创建JavaScript引擎: {}", engine.getClass().getName());
+        log.info("成功创建JavaScript引擎: {}", engine.getClass().getName());
 
         // 默认使用JS沙箱
         if (sandbox == null) {
@@ -100,7 +99,7 @@ public class JsScriptEngine extends AbstractScriptEngine {
                 // 执行脚本
                 return engine.eval(script, bindings);
             } catch (ScriptException e) {
-                logger.error("执行JavaScript脚本异常: {}", e.getMessage());
+                log.error("执行JavaScript脚本异常: {}", e.getMessage());
                 throw new RuntimeException("脚本执行异常: " + e.getMessage(), e);
             }
         };
@@ -109,7 +108,7 @@ public class JsScriptEngine extends AbstractScriptEngine {
             // 使用超时执行器执行脚本
             return ScriptUtils.executeWithTimeout(task, DEFAULT_TIMEOUT_MS);
         } catch (Exception e) {
-            logger.error("执行JavaScript脚本错误: {}", e.getMessage());
+            log.error("执行JavaScript脚本错误: {}", e.getMessage());
             throw new RuntimeException("脚本执行失败: " + e.getMessage(), e);
         }
     }
@@ -137,7 +136,7 @@ public class JsScriptEngine extends AbstractScriptEngine {
                 // 执行脚本
                 return engine.eval(script, bindings);
             } catch (ScriptException e) {
-                logger.error("执行JavaScript脚本异常: {}", e.getMessage());
+                log.error("执行JavaScript脚本异常: {}", e.getMessage());
                 throw new RuntimeException("脚本执行异常: " + e.getMessage(), e);
             }
         };
@@ -146,14 +145,14 @@ public class JsScriptEngine extends AbstractScriptEngine {
             // 使用超时执行器执行脚本
             return ScriptUtils.executeWithTimeout(task, DEFAULT_TIMEOUT_MS);
         } catch (Exception e) {
-            logger.error("执行JavaScript脚本错误: {}", e.getMessage());
+            log.error("执行JavaScript脚本错误: {}", e.getMessage());
             throw new RuntimeException("脚本执行失败: " + e.getMessage(), e);
         }
     }
 
     @Override
     public void destroy() {
-        logger.info("销毁JavaScript脚本引擎");
+        log.info("销毁JavaScript脚本引擎");
         cachedScripts.clear();
         engine = null;
         engineManager = null;

+ 3 - 4
yudao-module-iot/yudao-module-iot-plugins/yudao-module-iot-plugin-script/src/main/java/cn/iocoder/yudao/module/iot/plugin/script/engine/ScriptEngineFactory.java

@@ -1,15 +1,14 @@
 package cn.iocoder.yudao.module.iot.plugin.script.engine;
 
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
+import lombok.extern.slf4j.Slf4j;
 import org.springframework.stereotype.Component;
 
 /**
  * 脚本引擎工厂,用于创建不同类型的脚本引擎
  */
 @Component
+@Slf4j
 public class ScriptEngineFactory {
-    private static final Logger logger = LoggerFactory.getLogger(ScriptEngineFactory.class);
 
     /**
      * 创建JavaScript脚本引擎
@@ -17,7 +16,7 @@ public class ScriptEngineFactory {
      * @return JavaScript脚本引擎
      */
     public JsScriptEngine createJsEngine() {
-        logger.debug("创建JavaScript脚本引擎");
+        log.debug("创建JavaScript脚本引擎");
         return new JsScriptEngine();
     }
 

+ 7 - 8
yudao-module-iot/yudao-module-iot-plugins/yudao-module-iot-plugin-script/src/main/java/cn/iocoder/yudao/module/iot/plugin/script/sandbox/JsSandbox.java

@@ -1,7 +1,6 @@
 package cn.iocoder.yudao.module.iot.plugin.script.sandbox;
 
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
+import lombok.extern.slf4j.Slf4j;
 
 import javax.script.ScriptEngine;
 import java.util.Arrays;
@@ -12,8 +11,8 @@ import java.util.regex.Pattern;
 /**
  * JavaScript脚本沙箱,限制脚本的执行权限
  */
+@Slf4j
 public class JsSandbox implements ScriptSandbox {
-    private static final Logger logger = LoggerFactory.getLogger(JsSandbox.class);
 
     /**
      * 禁止使用的关键字
@@ -59,10 +58,10 @@ public class JsSandbox implements ScriptSandbox {
             engine.eval("var Packages = undefined;");
 
             // 增强安全控制可以在这里添加
-            logger.debug("已应用JavaScript安全沙箱限制");
+            log.debug("已应用JavaScript安全沙箱限制");
 
         } catch (Exception e) {
-            logger.warn("应用JavaScript沙箱限制失败: {}", e.getMessage());
+            log.warn("应用JavaScript沙箱限制失败: {}", e.getMessage());
         }
     }
 
@@ -75,20 +74,20 @@ public class JsSandbox implements ScriptSandbox {
         // 检查禁止的关键字
         for (String keyword : FORBIDDEN_KEYWORDS) {
             if (script.contains(keyword)) {
-                logger.warn("脚本包含禁止使用的关键字: {}", keyword);
+                log.warn("脚本包含禁止使用的关键字: {}", keyword);
                 return false;
             }
         }
 
         // 使用正则表达式检查更复杂的模式
         if (FORBIDDEN_PATTERN.matcher(script).find()) {
-            logger.warn("脚本包含禁止使用的模式");
+            log.warn("脚本包含禁止使用的模式");
             return false;
         }
 
         // 脚本长度限制
         if (script.length() > 1024 * 100) { // 限制100KB
-            logger.warn("脚本太大,超过了限制");
+            log.warn("脚本太大,超过了限制");
             return false;
         }
 

+ 7 - 8
yudao-module-iot/yudao-module-iot-plugins/yudao-module-iot-plugin-script/src/main/java/cn/iocoder/yudao/module/iot/plugin/script/service/ScriptServiceImpl.java

@@ -6,13 +6,12 @@ import cn.iocoder.yudao.module.iot.plugin.script.engine.AbstractScriptEngine;
 import cn.iocoder.yudao.module.iot.plugin.script.engine.ScriptEngineFactory;
 import cn.iocoder.yudao.module.iot.plugin.script.sandbox.JsSandbox;
 import cn.iocoder.yudao.module.iot.plugin.script.sandbox.ScriptSandbox;
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
-import org.springframework.beans.factory.annotation.Autowired;
+import lombok.extern.slf4j.Slf4j;
 import org.springframework.stereotype.Service;
 
 import javax.annotation.PostConstruct;
 import javax.annotation.PreDestroy;
+import javax.annotation.Resource;
 import java.util.Map;
 import java.util.concurrent.ConcurrentHashMap;
 
@@ -20,10 +19,10 @@ import java.util.concurrent.ConcurrentHashMap;
  * 脚本服务实现类
  */
 @Service
+@Slf4j
 public class ScriptServiceImpl implements ScriptService {
-    private static final Logger logger = LoggerFactory.getLogger(ScriptServiceImpl.class);
 
-    @Autowired
+    @Resource
     private ScriptEngineFactory engineFactory;
 
     /**
@@ -50,7 +49,7 @@ public class ScriptServiceImpl implements ScriptService {
             try {
                 engine.destroy();
             } catch (Exception e) {
-                logger.error("销毁脚本引擎失败", e);
+                log.error("销毁脚本引擎失败", e);
             }
         }
         engineCache.clear();
@@ -75,7 +74,7 @@ public class ScriptServiceImpl implements ScriptService {
             // 执行脚本
             return engine.execute(script, context);
         } catch (Exception e) {
-            logger.error("执行脚本失败: {}", e.getMessage());
+            log.error("执行脚本失败: {}", e.getMessage());
             throw new RuntimeException("执行脚本失败: " + e.getMessage(), e);
         }
     }
@@ -101,7 +100,7 @@ public class ScriptServiceImpl implements ScriptService {
     public boolean validateScript(String scriptType, String script) {
         ScriptSandbox sandbox = sandboxCache.get(scriptType.toLowerCase());
         if (sandbox == null) {
-            logger.warn("找不到脚本类型[{}]对应的沙箱,使用默认JS沙箱", scriptType);
+            log.warn("找不到脚本类型[{}]对应的沙箱,使用默认JS沙箱", scriptType);
             sandbox = new JsSandbox();
             sandboxCache.put(scriptType.toLowerCase(), sandbox);
         }

+ 7 - 8
yudao-module-iot/yudao-module-iot-plugins/yudao-module-iot-plugin-script/src/main/java/cn/iocoder/yudao/module/iot/plugin/script/util/ScriptUtils.java

@@ -1,8 +1,7 @@
 package cn.iocoder.yudao.module.iot.plugin.script.util;
 
 import cn.hutool.json.JSONUtil;
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
+import lombok.extern.slf4j.Slf4j;
 
 import java.util.Map;
 import java.util.concurrent.*;
@@ -10,8 +9,8 @@ import java.util.concurrent.*;
 /**
  * 脚本工具类,提供执行脚本的辅助方法
  */
+@Slf4j
 public class ScriptUtils {
-    private static final Logger logger = LoggerFactory.getLogger(ScriptUtils.class);
 
     /**
      * 默认脚本执行超时时间(毫秒)
@@ -90,7 +89,7 @@ public class ScriptUtils {
             // 使用hutool的JSONUtil工具类解析JSON
             return JSONUtil.toBean(json, Map.class);
         } catch (Exception e) {
-            logger.error("解析JSON失败: {}", e.getMessage());
+            log.error("解析JSON失败: {}", e.getMessage());
             return null;
         }
     }
@@ -114,12 +113,12 @@ public class ScriptUtils {
             try {
                 return Integer.parseInt((String) obj);
             } catch (NumberFormatException e) {
-                logger.debug("无法将字符串转换为整数: {}", obj);
+                log.debug("无法将字符串转换为整数: {}", obj);
                 return null;
             }
         }
 
-        logger.debug("无法将对象转换为整数: {}", obj.getClass().getName());
+        log.debug("无法将对象转换为整数: {}", obj.getClass().getName());
         return null;
     }
 
@@ -142,12 +141,12 @@ public class ScriptUtils {
             try {
                 return Double.parseDouble((String) obj);
             } catch (NumberFormatException e) {
-                logger.debug("无法将字符串转换为双精度浮点数: {}", obj);
+                log.debug("无法将字符串转换为双精度浮点数: {}", obj);
                 return null;
             }
         }
 
-        logger.debug("无法将对象转换为双精度浮点数: {}", obj.getClass().getName());
+        log.debug("无法将对象转换为双精度浮点数: {}", obj.getClass().getName());
         return null;
     }