【功能优化】IoT: 重构脚本服务实现,启用脚本执行和测试逻辑,更新日志记录方式
This commit is contained in:
parent
a9dc654b36
commit
0ae893272b
|
@ -70,11 +70,11 @@
|
||||||
</dependency>
|
</dependency>
|
||||||
|
|
||||||
<!-- 脚本插件相关 -->
|
<!-- 脚本插件相关 -->
|
||||||
<!--<dependency>
|
<dependency>
|
||||||
<groupId>cn.iocoder.boot</groupId>
|
<groupId>cn.iocoder.boot</groupId>
|
||||||
<artifactId>yudao-module-iot-plugin-script</artifactId>
|
<artifactId>yudao-module-iot-plugin-script</artifactId>
|
||||||
<version>${revision}</version>
|
<version>${revision}</version>
|
||||||
</dependency>-->
|
</dependency>
|
||||||
|
|
||||||
<!-- 消息队列相关 -->
|
<!-- 消息队列相关 -->
|
||||||
<dependency>
|
<dependency>
|
||||||
|
|
|
@ -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.IotProductDO;
|
||||||
import cn.iocoder.yudao.module.iot.dal.dataobject.product.IotProductScriptDO;
|
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.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 com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
|
||||||
import jakarta.annotation.Resource;
|
import jakarta.annotation.Resource;
|
||||||
import lombok.extern.slf4j.Slf4j;
|
import lombok.extern.slf4j.Slf4j;
|
||||||
|
@ -40,8 +42,8 @@ public class IotProductScriptServiceImpl implements IotProductScriptService {
|
||||||
@Resource
|
@Resource
|
||||||
private IotProductService productService;
|
private IotProductService productService;
|
||||||
|
|
||||||
// @Resource
|
@Resource
|
||||||
// private ScriptService scriptService;
|
private ScriptService scriptService;
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Long createProductScript(IotProductScriptSaveReqVO createReqVO) {
|
public Long createProductScript(IotProductScriptSaveReqVO createReqVO) {
|
||||||
|
@ -118,90 +120,89 @@ public class IotProductScriptServiceImpl implements IotProductScriptService {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public IotProductScriptTestRespVO testProductScript(IotProductScriptTestReqVO testReqVO) {
|
public IotProductScriptTestRespVO testProductScript(IotProductScriptTestReqVO testReqVO) {
|
||||||
// long startTime = System.currentTimeMillis();
|
long startTime = System.currentTimeMillis();
|
||||||
//
|
|
||||||
// try {
|
try {
|
||||||
// // 验证产品是否存在
|
// 验证产品是否存在
|
||||||
// validateProductExists(testReqVO.getProductId());
|
validateProductExists(testReqVO.getProductId());
|
||||||
//
|
|
||||||
// // 根据ID获取已保存的脚本(如果有)
|
// 根据ID获取已保存的脚本(如果有)
|
||||||
// IotProductScriptDO existingScript = null;
|
IotProductScriptDO existingScript = null;
|
||||||
// if (testReqVO.getId() != null) {
|
if (testReqVO.getId() != null) {
|
||||||
// existingScript = getProductScript(testReqVO.getId());
|
existingScript = getProductScript(testReqVO.getId());
|
||||||
// }
|
}
|
||||||
//
|
|
||||||
// // 创建测试上下文
|
// 创建测试上下文
|
||||||
// PluginScriptContext context = new PluginScriptContext();
|
PluginScriptContext context = new PluginScriptContext();
|
||||||
// IotProductDO product = productService.getProduct(testReqVO.getProductId());
|
IotProductDO product = productService.getProduct(testReqVO.getProductId());
|
||||||
//
|
|
||||||
// // 设置设备上下文(使用产品信息,没有具体设备)
|
// 设置设备上下文(使用产品信息,没有具体设备)
|
||||||
// context.withDeviceContext(product.getProductKey(), null);
|
context.withDeviceContext(product.getProductKey(), null);
|
||||||
//
|
|
||||||
// // 设置输入参数
|
// 设置输入参数
|
||||||
// Map<String, Object> params = new HashMap<>();
|
Map<String, Object> params = new HashMap<>();
|
||||||
// params.put("input", testReqVO.getTestInput());
|
params.put("input", testReqVO.getTestInput());
|
||||||
// params.put("productKey", product.getProductKey());
|
params.put("productKey", product.getProductKey());
|
||||||
// params.put("scriptType", testReqVO.getScriptType());
|
params.put("scriptType", testReqVO.getScriptType());
|
||||||
//
|
|
||||||
// // 根据脚本类型设置特定参数
|
// 根据脚本类型设置特定参数
|
||||||
// switch (testReqVO.getScriptType()) {
|
switch (testReqVO.getScriptType()) {
|
||||||
// case 1: // PROPERTY_PARSER
|
case 1: // PROPERTY_PARSER
|
||||||
// params.put("method", "property");
|
params.put("method", "property");
|
||||||
// break;
|
break;
|
||||||
// case 2: // EVENT_PARSER
|
case 2: // EVENT_PARSER
|
||||||
// params.put("method", "event");
|
params.put("method", "event");
|
||||||
// params.put("identifier", "default");
|
params.put("identifier", "default");
|
||||||
// break;
|
break;
|
||||||
// case 3: // COMMAND_ENCODER
|
case 3: // COMMAND_ENCODER
|
||||||
// params.put("method", "command");
|
params.put("method", "command");
|
||||||
// break;
|
break;
|
||||||
// default:
|
default:
|
||||||
// // 默认不添加额外参数
|
// 默认不添加额外参数
|
||||||
// }
|
}
|
||||||
//
|
|
||||||
// // 添加所有参数到上下文
|
// 添加所有参数到上下文
|
||||||
// for (Map.Entry<String, Object> entry : params.entrySet()) {
|
for (Map.Entry<String, Object> entry : params.entrySet()) {
|
||||||
// context.setParameter(entry.getKey(), entry.getValue());
|
context.setParameter(entry.getKey(), entry.getValue());
|
||||||
// }
|
}
|
||||||
//
|
|
||||||
// // 执行脚本
|
// 执行脚本
|
||||||
// Object result = scriptService.executeScript(
|
Object result = scriptService.executeScript(
|
||||||
// testReqVO.getScriptLanguage(),
|
testReqVO.getScriptLanguage(),
|
||||||
// testReqVO.getScriptContent(),
|
testReqVO.getScriptContent(),
|
||||||
// context);
|
context);
|
||||||
//
|
|
||||||
// // 更新测试结果(如果是已保存的脚本)
|
// 更新测试结果(如果是已保存的脚本)
|
||||||
// if (existingScript != null) {
|
if (existingScript != null) {
|
||||||
// IotProductScriptDO updateObj = new IotProductScriptDO();
|
IotProductScriptDO updateObj = new IotProductScriptDO();
|
||||||
// updateObj.setId(existingScript.getId());
|
updateObj.setId(existingScript.getId());
|
||||||
// updateObj.setLastTestTime(LocalDateTime.now());
|
updateObj.setLastTestTime(LocalDateTime.now());
|
||||||
// updateObj.setLastTestResult(1); // 1表示成功
|
updateObj.setLastTestResult(1); // 1表示成功
|
||||||
// productScriptMapper.updateById(updateObj);
|
productScriptMapper.updateById(updateObj);
|
||||||
// }
|
}
|
||||||
//
|
|
||||||
// long executionTime = System.currentTimeMillis() - startTime;
|
long executionTime = System.currentTimeMillis() - startTime;
|
||||||
// return IotProductScriptTestRespVO.success(result, executionTime);
|
return IotProductScriptTestRespVO.success(result, executionTime);
|
||||||
//
|
|
||||||
// } catch (Exception e) {
|
} catch (Exception e) {
|
||||||
// log.error("[testProductScript][测试脚本异常]", e);
|
log.error("[testProductScript][测试脚本异常]", e);
|
||||||
//
|
|
||||||
// // 如果是已保存的脚本,更新测试失败状态
|
// 如果是已保存的脚本,更新测试失败状态
|
||||||
// if (testReqVO.getId() != null) {
|
if (testReqVO.getId() != null) {
|
||||||
// try {
|
try {
|
||||||
// IotProductScriptDO updateObj = new IotProductScriptDO();
|
IotProductScriptDO updateObj = new IotProductScriptDO();
|
||||||
// updateObj.setId(testReqVO.getId());
|
updateObj.setId(testReqVO.getId());
|
||||||
// updateObj.setLastTestTime(LocalDateTime.now());
|
updateObj.setLastTestTime(LocalDateTime.now());
|
||||||
// updateObj.setLastTestResult(0); // 0表示失败
|
updateObj.setLastTestResult(0); // 0表示失败
|
||||||
// productScriptMapper.updateById(updateObj);
|
productScriptMapper.updateById(updateObj);
|
||||||
// } catch (Exception ex) {
|
} catch (Exception ex) {
|
||||||
// log.error("[testProductScript][更新脚本测试结果异常]", ex);
|
log.error("[testProductScript][更新脚本测试结果异常]", ex);
|
||||||
// }
|
}
|
||||||
// }
|
}
|
||||||
//
|
|
||||||
// long executionTime = System.currentTimeMillis() - startTime;
|
long executionTime = System.currentTimeMillis() - startTime;
|
||||||
// return IotProductScriptTestRespVO.error(e.getMessage(), executionTime);
|
return IotProductScriptTestRespVO.error(e.getMessage(), executionTime);
|
||||||
// }
|
}
|
||||||
return null;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
|
|
@ -19,7 +19,7 @@
|
||||||
<!-- 引入公共模块 -->
|
<!-- 引入公共模块 -->
|
||||||
<dependency>
|
<dependency>
|
||||||
<groupId>cn.iocoder.boot</groupId>
|
<groupId>cn.iocoder.boot</groupId>
|
||||||
<artifactId>yudao-module-iot-plugin-common</artifactId>
|
<artifactId>yudao-module-iot-api</artifactId>
|
||||||
<version>${revision}</version>
|
<version>${revision}</version>
|
||||||
</dependency>
|
</dependency>
|
||||||
|
|
||||||
|
|
|
@ -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.context.PluginScriptContext;
|
||||||
import cn.iocoder.yudao.module.iot.plugin.script.service.ScriptService;
|
import cn.iocoder.yudao.module.iot.plugin.script.service.ScriptService;
|
||||||
import org.slf4j.Logger;
|
import lombok.extern.slf4j.Slf4j;
|
||||||
import org.slf4j.LoggerFactory;
|
|
||||||
import org.springframework.beans.factory.annotation.Autowired;
|
import org.springframework.beans.factory.annotation.Autowired;
|
||||||
import org.springframework.stereotype.Component;
|
import org.springframework.stereotype.Component;
|
||||||
|
|
||||||
|
@ -14,8 +13,8 @@ import java.util.Map;
|
||||||
* 脚本使用示例类
|
* 脚本使用示例类
|
||||||
*/
|
*/
|
||||||
@Component
|
@Component
|
||||||
|
@Slf4j
|
||||||
public class ScriptExample {
|
public class ScriptExample {
|
||||||
private static final Logger logger = LoggerFactory.getLogger(ScriptExample.class);
|
|
||||||
|
|
||||||
@Autowired
|
@Autowired
|
||||||
private ScriptService scriptService;
|
private ScriptService scriptService;
|
||||||
|
@ -31,7 +30,7 @@ public class ScriptExample {
|
||||||
params.put("b", 20);
|
params.put("b", 20);
|
||||||
|
|
||||||
Object result = scriptService.executeJavaScript(script, params);
|
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);
|
Object result = scriptService.executeJavaScript(script, context);
|
||||||
if (result != null) {
|
if (result != null) {
|
||||||
// 处理结果
|
// 处理结果
|
||||||
logger.info("设备数据处理结果: {}", result);
|
log.info("设备数据处理结果: {}", result);
|
||||||
|
|
||||||
// 安全地将结果转换为Map
|
// 安全地将结果转换为Map
|
||||||
if (result instanceof Map) {
|
if (result instanceof Map) {
|
||||||
return (Map<String, Object>) result;
|
return (Map<String, Object>) result;
|
||||||
} else {
|
} else {
|
||||||
logger.warn("脚本返回结果类型不是Map: {}", result.getClass().getName());
|
log.warn("脚本返回结果类型不是Map: {}", result.getClass().getName());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
logger.error("处理设备数据失败: {}", e.getMessage());
|
log.error("处理设备数据失败: {}", e.getMessage());
|
||||||
}
|
}
|
||||||
|
|
||||||
return new HashMap<>();
|
return new HashMap<>();
|
||||||
|
@ -121,10 +120,10 @@ public class ScriptExample {
|
||||||
if (result instanceof String) {
|
if (result instanceof String) {
|
||||||
return (String) result;
|
return (String) result;
|
||||||
} else if (result != null) {
|
} else if (result != null) {
|
||||||
logger.warn("脚本返回结果类型不是String: {}", result.getClass().getName());
|
log.warn("脚本返回结果类型不是String: {}", result.getClass().getName());
|
||||||
}
|
}
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
logger.error("生成设备命令失败: {}", e.getMessage());
|
log.error("生成设备命令失败: {}", e.getMessage());
|
||||||
}
|
}
|
||||||
|
|
||||||
return null;
|
return null;
|
||||||
|
|
|
@ -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.context.ScriptContext;
|
||||||
import cn.iocoder.yudao.module.iot.plugin.script.sandbox.JsSandbox;
|
import cn.iocoder.yudao.module.iot.plugin.script.sandbox.JsSandbox;
|
||||||
import cn.iocoder.yudao.module.iot.plugin.script.util.ScriptUtils;
|
import cn.iocoder.yudao.module.iot.plugin.script.util.ScriptUtils;
|
||||||
import org.slf4j.Logger;
|
import lombok.extern.slf4j.Slf4j;
|
||||||
import org.slf4j.LoggerFactory;
|
|
||||||
|
|
||||||
import javax.script.*;
|
import javax.script.*;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
|
@ -16,8 +15,8 @@ import java.util.concurrent.ConcurrentHashMap;
|
||||||
* JavaScript脚本引擎实现
|
* JavaScript脚本引擎实现
|
||||||
* 使用JSR-223 Nashorn脚本引擎
|
* 使用JSR-223 Nashorn脚本引擎
|
||||||
*/
|
*/
|
||||||
|
@Slf4j
|
||||||
public class JsScriptEngine extends AbstractScriptEngine {
|
public class JsScriptEngine extends AbstractScriptEngine {
|
||||||
private static final Logger logger = LoggerFactory.getLogger(JsScriptEngine.class);
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 默认脚本执行超时时间(毫秒)
|
* 默认脚本执行超时时间(毫秒)
|
||||||
|
@ -46,7 +45,7 @@ public class JsScriptEngine extends AbstractScriptEngine {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void init() {
|
public void init() {
|
||||||
logger.info("初始化JavaScript脚本引擎");
|
log.info("初始化JavaScript脚本引擎");
|
||||||
|
|
||||||
// 创建脚本引擎管理器
|
// 创建脚本引擎管理器
|
||||||
engineManager = new ScriptEngineManager();
|
engineManager = new ScriptEngineManager();
|
||||||
|
@ -54,7 +53,7 @@ public class JsScriptEngine extends AbstractScriptEngine {
|
||||||
// 获取JavaScript引擎
|
// 获取JavaScript引擎
|
||||||
engine = engineManager.getEngineByName(JS_ENGINE_NAME);
|
engine = engineManager.getEngineByName(JS_ENGINE_NAME);
|
||||||
if (engine == null) {
|
if (engine == null) {
|
||||||
logger.error("无法创建JavaScript引擎,尝试使用JavaScript名称获取");
|
log.error("无法创建JavaScript引擎,尝试使用JavaScript名称获取");
|
||||||
engine = engineManager.getEngineByName("JavaScript");
|
engine = engineManager.getEngineByName("JavaScript");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -62,7 +61,7 @@ public class JsScriptEngine extends AbstractScriptEngine {
|
||||||
throw new IllegalStateException("无法创建JavaScript引擎,请检查环境配置");
|
throw new IllegalStateException("无法创建JavaScript引擎,请检查环境配置");
|
||||||
}
|
}
|
||||||
|
|
||||||
logger.info("成功创建JavaScript引擎: {}", engine.getClass().getName());
|
log.info("成功创建JavaScript引擎: {}", engine.getClass().getName());
|
||||||
|
|
||||||
// 默认使用JS沙箱
|
// 默认使用JS沙箱
|
||||||
if (sandbox == null) {
|
if (sandbox == null) {
|
||||||
|
@ -100,7 +99,7 @@ public class JsScriptEngine extends AbstractScriptEngine {
|
||||||
// 执行脚本
|
// 执行脚本
|
||||||
return engine.eval(script, bindings);
|
return engine.eval(script, bindings);
|
||||||
} catch (ScriptException e) {
|
} catch (ScriptException e) {
|
||||||
logger.error("执行JavaScript脚本异常: {}", e.getMessage());
|
log.error("执行JavaScript脚本异常: {}", e.getMessage());
|
||||||
throw new RuntimeException("脚本执行异常: " + e.getMessage(), e);
|
throw new RuntimeException("脚本执行异常: " + e.getMessage(), e);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
@ -109,7 +108,7 @@ public class JsScriptEngine extends AbstractScriptEngine {
|
||||||
// 使用超时执行器执行脚本
|
// 使用超时执行器执行脚本
|
||||||
return ScriptUtils.executeWithTimeout(task, DEFAULT_TIMEOUT_MS);
|
return ScriptUtils.executeWithTimeout(task, DEFAULT_TIMEOUT_MS);
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
logger.error("执行JavaScript脚本错误: {}", e.getMessage());
|
log.error("执行JavaScript脚本错误: {}", e.getMessage());
|
||||||
throw new RuntimeException("脚本执行失败: " + e.getMessage(), e);
|
throw new RuntimeException("脚本执行失败: " + e.getMessage(), e);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -137,7 +136,7 @@ public class JsScriptEngine extends AbstractScriptEngine {
|
||||||
// 执行脚本
|
// 执行脚本
|
||||||
return engine.eval(script, bindings);
|
return engine.eval(script, bindings);
|
||||||
} catch (ScriptException e) {
|
} catch (ScriptException e) {
|
||||||
logger.error("执行JavaScript脚本异常: {}", e.getMessage());
|
log.error("执行JavaScript脚本异常: {}", e.getMessage());
|
||||||
throw new RuntimeException("脚本执行异常: " + e.getMessage(), e);
|
throw new RuntimeException("脚本执行异常: " + e.getMessage(), e);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
@ -146,14 +145,14 @@ public class JsScriptEngine extends AbstractScriptEngine {
|
||||||
// 使用超时执行器执行脚本
|
// 使用超时执行器执行脚本
|
||||||
return ScriptUtils.executeWithTimeout(task, DEFAULT_TIMEOUT_MS);
|
return ScriptUtils.executeWithTimeout(task, DEFAULT_TIMEOUT_MS);
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
logger.error("执行JavaScript脚本错误: {}", e.getMessage());
|
log.error("执行JavaScript脚本错误: {}", e.getMessage());
|
||||||
throw new RuntimeException("脚本执行失败: " + e.getMessage(), e);
|
throw new RuntimeException("脚本执行失败: " + e.getMessage(), e);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void destroy() {
|
public void destroy() {
|
||||||
logger.info("销毁JavaScript脚本引擎");
|
log.info("销毁JavaScript脚本引擎");
|
||||||
cachedScripts.clear();
|
cachedScripts.clear();
|
||||||
engine = null;
|
engine = null;
|
||||||
engineManager = null;
|
engineManager = null;
|
||||||
|
|
|
@ -1,15 +1,14 @@
|
||||||
package cn.iocoder.yudao.module.iot.plugin.script.engine;
|
package cn.iocoder.yudao.module.iot.plugin.script.engine;
|
||||||
|
|
||||||
import org.slf4j.Logger;
|
import lombok.extern.slf4j.Slf4j;
|
||||||
import org.slf4j.LoggerFactory;
|
|
||||||
import org.springframework.stereotype.Component;
|
import org.springframework.stereotype.Component;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 脚本引擎工厂,用于创建不同类型的脚本引擎
|
* 脚本引擎工厂,用于创建不同类型的脚本引擎
|
||||||
*/
|
*/
|
||||||
@Component
|
@Component
|
||||||
|
@Slf4j
|
||||||
public class ScriptEngineFactory {
|
public class ScriptEngineFactory {
|
||||||
private static final Logger logger = LoggerFactory.getLogger(ScriptEngineFactory.class);
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 创建JavaScript脚本引擎
|
* 创建JavaScript脚本引擎
|
||||||
|
@ -17,7 +16,7 @@ public class ScriptEngineFactory {
|
||||||
* @return JavaScript脚本引擎
|
* @return JavaScript脚本引擎
|
||||||
*/
|
*/
|
||||||
public JsScriptEngine createJsEngine() {
|
public JsScriptEngine createJsEngine() {
|
||||||
logger.debug("创建JavaScript脚本引擎");
|
log.debug("创建JavaScript脚本引擎");
|
||||||
return new JsScriptEngine();
|
return new JsScriptEngine();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1,7 +1,6 @@
|
||||||
package cn.iocoder.yudao.module.iot.plugin.script.sandbox;
|
package cn.iocoder.yudao.module.iot.plugin.script.sandbox;
|
||||||
|
|
||||||
import org.slf4j.Logger;
|
import lombok.extern.slf4j.Slf4j;
|
||||||
import org.slf4j.LoggerFactory;
|
|
||||||
|
|
||||||
import javax.script.ScriptEngine;
|
import javax.script.ScriptEngine;
|
||||||
import java.util.Arrays;
|
import java.util.Arrays;
|
||||||
|
@ -12,8 +11,8 @@ import java.util.regex.Pattern;
|
||||||
/**
|
/**
|
||||||
* JavaScript脚本沙箱,限制脚本的执行权限
|
* JavaScript脚本沙箱,限制脚本的执行权限
|
||||||
*/
|
*/
|
||||||
|
@Slf4j
|
||||||
public class JsSandbox implements ScriptSandbox {
|
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;");
|
engine.eval("var Packages = undefined;");
|
||||||
|
|
||||||
// 增强安全控制可以在这里添加
|
// 增强安全控制可以在这里添加
|
||||||
logger.debug("已应用JavaScript安全沙箱限制");
|
log.debug("已应用JavaScript安全沙箱限制");
|
||||||
|
|
||||||
} catch (Exception e) {
|
} 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) {
|
for (String keyword : FORBIDDEN_KEYWORDS) {
|
||||||
if (script.contains(keyword)) {
|
if (script.contains(keyword)) {
|
||||||
logger.warn("脚本包含禁止使用的关键字: {}", keyword);
|
log.warn("脚本包含禁止使用的关键字: {}", keyword);
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// 使用正则表达式检查更复杂的模式
|
// 使用正则表达式检查更复杂的模式
|
||||||
if (FORBIDDEN_PATTERN.matcher(script).find()) {
|
if (FORBIDDEN_PATTERN.matcher(script).find()) {
|
||||||
logger.warn("脚本包含禁止使用的模式");
|
log.warn("脚本包含禁止使用的模式");
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
// 脚本长度限制
|
// 脚本长度限制
|
||||||
if (script.length() > 1024 * 100) { // 限制100KB
|
if (script.length() > 1024 * 100) { // 限制100KB
|
||||||
logger.warn("脚本太大,超过了限制");
|
log.warn("脚本太大,超过了限制");
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -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.engine.ScriptEngineFactory;
|
||||||
import cn.iocoder.yudao.module.iot.plugin.script.sandbox.JsSandbox;
|
import cn.iocoder.yudao.module.iot.plugin.script.sandbox.JsSandbox;
|
||||||
import cn.iocoder.yudao.module.iot.plugin.script.sandbox.ScriptSandbox;
|
import cn.iocoder.yudao.module.iot.plugin.script.sandbox.ScriptSandbox;
|
||||||
import org.slf4j.Logger;
|
import lombok.extern.slf4j.Slf4j;
|
||||||
import org.slf4j.LoggerFactory;
|
|
||||||
import org.springframework.beans.factory.annotation.Autowired;
|
|
||||||
import org.springframework.stereotype.Service;
|
import org.springframework.stereotype.Service;
|
||||||
|
|
||||||
import javax.annotation.PostConstruct;
|
import javax.annotation.PostConstruct;
|
||||||
import javax.annotation.PreDestroy;
|
import javax.annotation.PreDestroy;
|
||||||
|
import javax.annotation.Resource;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
import java.util.concurrent.ConcurrentHashMap;
|
import java.util.concurrent.ConcurrentHashMap;
|
||||||
|
|
||||||
|
@ -20,10 +19,10 @@ import java.util.concurrent.ConcurrentHashMap;
|
||||||
* 脚本服务实现类
|
* 脚本服务实现类
|
||||||
*/
|
*/
|
||||||
@Service
|
@Service
|
||||||
|
@Slf4j
|
||||||
public class ScriptServiceImpl implements ScriptService {
|
public class ScriptServiceImpl implements ScriptService {
|
||||||
private static final Logger logger = LoggerFactory.getLogger(ScriptServiceImpl.class);
|
|
||||||
|
|
||||||
@Autowired
|
@Resource
|
||||||
private ScriptEngineFactory engineFactory;
|
private ScriptEngineFactory engineFactory;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -50,7 +49,7 @@ public class ScriptServiceImpl implements ScriptService {
|
||||||
try {
|
try {
|
||||||
engine.destroy();
|
engine.destroy();
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
logger.error("销毁脚本引擎失败", e);
|
log.error("销毁脚本引擎失败", e);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
engineCache.clear();
|
engineCache.clear();
|
||||||
|
@ -75,7 +74,7 @@ public class ScriptServiceImpl implements ScriptService {
|
||||||
// 执行脚本
|
// 执行脚本
|
||||||
return engine.execute(script, context);
|
return engine.execute(script, context);
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
logger.error("执行脚本失败: {}", e.getMessage());
|
log.error("执行脚本失败: {}", e.getMessage());
|
||||||
throw new RuntimeException("执行脚本失败: " + e.getMessage(), e);
|
throw new RuntimeException("执行脚本失败: " + e.getMessage(), e);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -101,7 +100,7 @@ public class ScriptServiceImpl implements ScriptService {
|
||||||
public boolean validateScript(String scriptType, String script) {
|
public boolean validateScript(String scriptType, String script) {
|
||||||
ScriptSandbox sandbox = sandboxCache.get(scriptType.toLowerCase());
|
ScriptSandbox sandbox = sandboxCache.get(scriptType.toLowerCase());
|
||||||
if (sandbox == null) {
|
if (sandbox == null) {
|
||||||
logger.warn("找不到脚本类型[{}]对应的沙箱,使用默认JS沙箱", scriptType);
|
log.warn("找不到脚本类型[{}]对应的沙箱,使用默认JS沙箱", scriptType);
|
||||||
sandbox = new JsSandbox();
|
sandbox = new JsSandbox();
|
||||||
sandboxCache.put(scriptType.toLowerCase(), sandbox);
|
sandboxCache.put(scriptType.toLowerCase(), sandbox);
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,8 +1,7 @@
|
||||||
package cn.iocoder.yudao.module.iot.plugin.script.util;
|
package cn.iocoder.yudao.module.iot.plugin.script.util;
|
||||||
|
|
||||||
import cn.hutool.json.JSONUtil;
|
import cn.hutool.json.JSONUtil;
|
||||||
import org.slf4j.Logger;
|
import lombok.extern.slf4j.Slf4j;
|
||||||
import org.slf4j.LoggerFactory;
|
|
||||||
|
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
import java.util.concurrent.*;
|
import java.util.concurrent.*;
|
||||||
|
@ -10,8 +9,8 @@ import java.util.concurrent.*;
|
||||||
/**
|
/**
|
||||||
* 脚本工具类,提供执行脚本的辅助方法
|
* 脚本工具类,提供执行脚本的辅助方法
|
||||||
*/
|
*/
|
||||||
|
@Slf4j
|
||||||
public class ScriptUtils {
|
public class ScriptUtils {
|
||||||
private static final Logger logger = LoggerFactory.getLogger(ScriptUtils.class);
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 默认脚本执行超时时间(毫秒)
|
* 默认脚本执行超时时间(毫秒)
|
||||||
|
@ -90,7 +89,7 @@ public class ScriptUtils {
|
||||||
// 使用hutool的JSONUtil工具类解析JSON
|
// 使用hutool的JSONUtil工具类解析JSON
|
||||||
return JSONUtil.toBean(json, Map.class);
|
return JSONUtil.toBean(json, Map.class);
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
logger.error("解析JSON失败: {}", e.getMessage());
|
log.error("解析JSON失败: {}", e.getMessage());
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -114,12 +113,12 @@ public class ScriptUtils {
|
||||||
try {
|
try {
|
||||||
return Integer.parseInt((String) obj);
|
return Integer.parseInt((String) obj);
|
||||||
} catch (NumberFormatException e) {
|
} catch (NumberFormatException e) {
|
||||||
logger.debug("无法将字符串转换为整数: {}", obj);
|
log.debug("无法将字符串转换为整数: {}", obj);
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
logger.debug("无法将对象转换为整数: {}", obj.getClass().getName());
|
log.debug("无法将对象转换为整数: {}", obj.getClass().getName());
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -142,12 +141,12 @@ public class ScriptUtils {
|
||||||
try {
|
try {
|
||||||
return Double.parseDouble((String) obj);
|
return Double.parseDouble((String) obj);
|
||||||
} catch (NumberFormatException e) {
|
} catch (NumberFormatException e) {
|
||||||
logger.debug("无法将字符串转换为双精度浮点数: {}", obj);
|
log.debug("无法将字符串转换为双精度浮点数: {}", obj);
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
logger.debug("无法将对象转换为双精度浮点数: {}", obj.getClass().getName());
|
log.debug("无法将对象转换为双精度浮点数: {}", obj.getClass().getName());
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue