Commit eeb1dc4a077b451652dddde2cb1628082917daef

Authored by YunaiV
1 parent 0ae89327

【代码评审】IoT:产品脚本的逻辑

Showing 20 changed files with 111 additions and 75 deletions
yudao-module-iot/yudao-module-iot-api/src/main/java/cn/iocoder/yudao/module/iot/enums/product/IotProductScriptLanguageEnum.java
... ... @@ -43,4 +43,5 @@ public enum IotProductScriptLanguageEnum implements ArrayValuable<String> {
43 43 .findFirst()
44 44 .orElse(null);
45 45 }
  46 +
46 47 }
47 48 \ No newline at end of file
... ...
yudao-module-iot/yudao-module-iot-api/src/main/java/cn/iocoder/yudao/module/iot/enums/product/IotProductScriptStatusEnum.java
... ... @@ -6,6 +6,7 @@ import lombok.Getter;
6 6  
7 7 import java.util.Arrays;
8 8  
  9 +// TODO @haohao:要不复用 commonstatus?
9 10 /**
10 11 * IoT 产品脚本状态枚举
11 12 *
... ...
yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/dataobject/product/IotProductScriptDO.java
... ... @@ -8,6 +8,7 @@ import lombok.*;
8 8  
9 9 import java.time.LocalDateTime;
10 10  
  11 +// TODO @haohao:类似阿里云的脚本,貌似是一个?这个可以简化么?【微信讨论哈】类似阿里云,貌似是加了个 topic?
11 12 /**
12 13 * IoT 产品脚本信息 DO
13 14 *
... ...
yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/product/IotProductScriptServiceImpl.java
... ... @@ -26,6 +26,7 @@ import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionU
26 26 import static cn.iocoder.yudao.module.iot.enums.ErrorCodeConstants.PRODUCT_NOT_EXISTS;
27 27 import static cn.iocoder.yudao.module.iot.enums.ErrorCodeConstants.PRODUCT_SCRIPT_NOT_EXISTS;
28 28  
  29 +// TODO @芋艿:后续再 review 哈!
29 30 /**
30 31 * IoT 产品脚本信息 Service 实现类
31 32 *
... ...
yudao-module-iot/yudao-module-iot-plugins/yudao-module-iot-plugin-http/src/main/java/cn/iocoder/yudao/module/iot/plugin/http/IotHttpPluginApplication.java
... ... @@ -5,17 +5,15 @@ import org.springframework.boot.SpringApplication;
5 5 import org.springframework.boot.WebApplicationType;
6 6 import org.springframework.boot.autoconfigure.SpringBootApplication;
7 7  
  8 +// TODO @芋艿:是不是搞成 cn.iocoder.yudao.module.iot.plugin?或者 common、script 要自动配置
8 9 /**
9 10 * 独立运行入口
10 11 */
11 12 @Slf4j
12 13 @SpringBootApplication(scanBasePackages = {
13   - // common 的包
14   - "cn.iocoder.yudao.module.iot.plugin.common",
15   - // http 的包
16   - "cn.iocoder.yudao.module.iot.plugin.http",
17   - // script 的包
18   - "cn.iocoder.yudao.module.iot.plugin.script"
  14 + "cn.iocoder.yudao.module.iot.plugin.common", // common 的包
  15 + "cn.iocoder.yudao.module.iot.plugin.http", // http 的包
  16 + "cn.iocoder.yudao.module.iot.plugin.script" // script 的包
19 17 })
20 18 public class IotHttpPluginApplication {
21 19  
... ...
yudao-module-iot/yudao-module-iot-plugins/yudao-module-iot-plugin-http/src/main/java/cn/iocoder/yudao/module/iot/plugin/http/script/HttpScriptService.java
... ... @@ -13,7 +13,7 @@ import java.util.Map;
13 13 import java.util.concurrent.ConcurrentHashMap;
14 14  
15 15 /**
16   - * HTTP协议脚本处理服务
  16 + * HTTP 协议脚本处理服务
17 17 * 用于管理和执行设备数据解析脚本
18 18 *
19 19 * @author haohao
... ... @@ -25,8 +25,10 @@ public class HttpScriptService {
25 25  
26 26 private final ScriptService scriptService;
27 27  
  28 + // TODO @haohao:后续可以考虑放到 guava 缓存
  29 + // TODO @haohao:可能要抽一个 script factory 之类的?方便多个 emqx、http 之类复用?
28 30 /**
29   - * 脚本缓存,按产品Key缓存脚本内容
  31 + * 脚本缓存,按产品 Key 缓存脚本内容
30 32 */
31 33 private final Map<String, String> scriptCache = new ConcurrentHashMap<>();
32 34  
... ... @@ -76,6 +78,7 @@ public class HttpScriptService {
76 78 productKey, deviceName, e);
77 79 }
78 80  
  81 + // TODO @芋艿:解析失败,是不是不能返回空?!
79 82 // 解析失败,返回空数据
80 83 return new HashMap<>();
81 84 }
... ... @@ -115,13 +118,14 @@ public class HttpScriptService {
115 118 productKey, deviceName, identifier, payload, result);
116 119  
117 120 // 处理结果
  121 + // TODO @haohao:处理结果,可以复用么?
118 122 if (result instanceof Map) {
119 123 return (Map<String, Object>) result;
120 124 } else if (result instanceof String) {
121 125 try {
122 126 return new JsonObject((String) result).getMap();
123 127 } catch (Exception e) {
124   - log.warn("[parseEventData][脚本返回的字符串不是有效的JSON] result:{}", result);
  128 + log.warn("[parseEventData][脚本返回的字符串不是有效的 JSON] result:{}", result);
125 129 }
126 130 }
127 131 } catch (Exception e) {
... ... @@ -129,6 +133,7 @@ public class HttpScriptService {
129 133 productKey, deviceName, identifier, e);
130 134 }
131 135  
  136 + // TODO @芋艿:解析失败,是不是不能返回空?!
132 137 // 解析失败,返回空数据
133 138 return new HashMap<>();
134 139 }
... ... @@ -191,10 +196,11 @@ public class HttpScriptService {
191 196 /**
192 197 * 设置产品解析脚本
193 198 *
194   - * @param productKey 产品Key
  199 + * @param productKey 产品 Key
195 200 * @param script 脚本内容
196 201 */
197 202 public void setScript(String productKey, String script) {
  203 + // TODO @haohao:if return 会好点哈
198 204 if (StrUtil.isNotBlank(productKey) && StrUtil.isNotBlank(script)) {
199 205 // 验证脚本是否有效
200 206 if (scriptService.validateScript("js", script)) {
... ... @@ -209,13 +215,14 @@ public class HttpScriptService {
209 215 /**
210 216 * 清除产品解析脚本
211 217 *
212   - * @param productKey 产品Key
  218 + * @param productKey 产品 Key
213 219 */
214 220 public void clearScript(String productKey) {
215   - if (StrUtil.isNotBlank(productKey)) {
216   - scriptCache.remove(productKey);
217   - log.info("[clearScript][清除产品:{}的解析脚本]", productKey);
  221 + if (StrUtil.isBlank(productKey)) {
  222 + return;
218 223 }
  224 + scriptCache.remove(productKey);
  225 + log.info("[clearScript][清除产品({})的解析脚本]", productKey);
219 226 }
220 227  
221 228 /**
... ... @@ -225,4 +232,5 @@ public class HttpScriptService {
225 232 scriptCache.clear();
226 233 log.info("[clearAllScripts][清除所有脚本缓存]");
227 234 }
  235 +
228 236 }
229 237 \ No newline at end of file
... ...
yudao-module-iot/yudao-module-iot-plugins/yudao-module-iot-plugin-http/src/main/java/cn/iocoder/yudao/module/iot/plugin/http/upstream/IotDeviceUpstreamServer.java
... ... @@ -35,8 +35,7 @@ public class IotDeviceUpstreamServer {
35 35 router.route().handler(BodyHandler.create()); // 处理 Body
36 36  
37 37 // 使用统一的 Handler 处理所有上行请求
38   - IotDeviceUpstreamVertxHandler upstreamHandler = new IotDeviceUpstreamVertxHandler(deviceUpstreamApi,
39   - applicationContext);
  38 + IotDeviceUpstreamVertxHandler upstreamHandler = new IotDeviceUpstreamVertxHandler(deviceUpstreamApi, applicationContext);
40 39 router.post(IotDeviceUpstreamVertxHandler.PROPERTY_PATH).handler(upstreamHandler);
41 40 router.post(IotDeviceUpstreamVertxHandler.EVENT_PATH).handler(upstreamHandler);
42 41  
... ...
yudao-module-iot/yudao-module-iot-plugins/yudao-module-iot-plugin-http/src/main/java/cn/iocoder/yudao/module/iot/plugin/http/upstream/router/IotDeviceUpstreamVertxHandler.java
1 1 package cn.iocoder.yudao.module.iot.plugin.http.upstream.router;
2 2  
  3 +import cn.hutool.core.collection.CollUtil;
3 4 import cn.hutool.core.util.IdUtil;
4 5 import cn.hutool.core.util.ObjUtil;
5 6 import cn.iocoder.yudao.framework.common.pojo.CommonResult;
... ... @@ -150,10 +151,11 @@ public class IotDeviceUpstreamVertxHandler implements Handler&lt;RoutingContext&gt; {
150 151 Map<String, Object> properties = scriptService.parsePropertyData(productKey, deviceName, body);
151 152  
152 153 // 如果脚本解析结果为空,使用默认解析逻辑
153   - if (properties.isEmpty()) {
  154 + // TODO @芋艿:注释说明一下,为什么要这么处理?
  155 + if (CollUtil.isNotEmpty(properties)) {
154 156 properties = new HashMap<>();
155   - Map<String, Object> params = body.getJsonObject("params") != null ? body.getJsonObject("params").getMap()
156   - : null;
  157 + Map<String, Object> params = body.getJsonObject("params") != null ?
  158 + body.getJsonObject("params").getMap() : null;
157 159 if (params != null) {
158 160 // 将标准格式的 params 转换为平台需要的 properties 格式
159 161 for (Map.Entry<String, Object> entry : params.entrySet()) {
... ... @@ -193,7 +195,7 @@ public class IotDeviceUpstreamVertxHandler implements Handler&lt;RoutingContext&gt; {
193 195 Map<String, Object> params = scriptService.parseEventData(productKey, deviceName, identifier, body);
194 196  
195 197 // 如果脚本解析结果为空,使用默认解析逻辑
196   - if (params.isEmpty()) {
  198 + if (CollUtil.isNotEmpty(params)) {
197 199 if (body.containsKey("params")) {
198 200 params = body.getJsonObject("params").getMap();
199 201 } else {
... ...
yudao-module-iot/yudao-module-iot-plugins/yudao-module-iot-plugin-script/src/main/java/cn/iocoder/yudao/module/iot/plugin/script/ScriptExample.java
... ... @@ -9,6 +9,7 @@ import org.springframework.stereotype.Component;
9 9 import java.util.HashMap;
10 10 import java.util.Map;
11 11  
  12 +// TODO @haohao:写到单测类里;
12 13 /**
13 14 * 脚本使用示例类
14 15 */
... ...
yudao-module-iot/yudao-module-iot-plugins/yudao-module-iot-plugin-script/src/main/java/cn/iocoder/yudao/module/iot/plugin/script/config/ScriptConfiguration.java
... ... @@ -6,6 +6,7 @@ import cn.iocoder.yudao.module.iot.plugin.script.service.ScriptServiceImpl;
6 6 import org.springframework.context.annotation.Bean;
7 7 import org.springframework.context.annotation.Configuration;
8 8  
  9 +// TODO @haohao:这个模块,是不是融合到 plugin-common 里哈?
9 10 /**
10 11 * 脚本模块配置类
11 12 */
... ... @@ -31,7 +32,7 @@ public class ScriptConfiguration {
31 32 @Bean
32 33 public ScriptService scriptService(ScriptEngineFactory engineFactory) {
33 34 ScriptServiceImpl service = new ScriptServiceImpl();
34   - // 如果有其他配置可以在这里设置
  35 + // TODO @haohao:如果有其他配置可以在这里设置
35 36 return service;
36 37 }
37   -}
38 38 \ No newline at end of file
  39 +}
39 40 \ No newline at end of file
... ...
yudao-module-iot/yudao-module-iot-plugins/yudao-module-iot-plugin-script/src/main/java/cn/iocoder/yudao/module/iot/plugin/script/context/PluginScriptContext.java
1 1 package cn.iocoder.yudao.module.iot.plugin.script.context;
2 2  
  3 +import lombok.Getter;
  4 +
3 5 import java.util.HashMap;
4 6 import java.util.Map;
5 7  
... ... @@ -11,18 +13,22 @@ public class PluginScriptContext implements ScriptContext {
11 13 /**
12 14 * 上下文参数
13 15 */
  16 + @Getter
14 17 private final Map<String, Object> parameters = new HashMap<>();
15 18  
16 19 /**
17 20 * 上下文函数
18 21 */
  22 + @Getter
19 23 private final Map<String, Object> functions = new HashMap<>();
20 24  
21 25 /**
22 26 * 日志函数接口
23 27 */
24 28 public interface LogFunction {
  29 +
25 30 void log(String message);
  31 +
26 32 }
27 33  
28 34 /**
... ... @@ -47,16 +53,6 @@ public class PluginScriptContext implements ScriptContext {
47 53 }
48 54  
49 55 @Override
50   - public Map<String, Object> getParameters() {
51   - return parameters;
52   - }
53   -
54   - @Override
55   - public Map<String, Object> getFunctions() {
56   - return functions;
57   - }
58   -
59   - @Override
60 56 public void setParameter(String key, Object value) {
61 57 parameters.put(key, value);
62 58 }
... ... @@ -71,6 +67,7 @@ public class PluginScriptContext implements ScriptContext {
71 67 functions.put(name, function);
72 68 }
73 69  
  70 + // TODO @haohao:setParameters?这样的话,with 都是一些比较个性的参数
74 71 /**
75 72 * 批量设置参数
76 73 *
... ... @@ -87,11 +84,13 @@ public class PluginScriptContext implements ScriptContext {
87 84 /**
88 85 * 添加设备相关的上下文参数
89 86 *
90   - * @param deviceId 设备ID
  87 + * @param deviceId 设备 ID
91 88 * @param deviceData 设备数据
92 89 * @return 当前上下文对象
93 90 */
  91 + // TODO @haohao:是不是加个 (String productKey, String deviceName, Map<String, Object> deviceData) {
94 92 public PluginScriptContext withDeviceContext(String deviceId, Map<String, Object> deviceData) {
  93 + // TODO @haohao:deviceId 一般是分开,还是合并哈?
95 94 parameters.put("deviceId", deviceId);
96 95 parameters.put("deviceData", deviceData);
97 96 return this;
... ... @@ -110,6 +109,7 @@ public class PluginScriptContext implements ScriptContext {
110 109 return this;
111 110 }
112 111  
  112 + // TODO @haohao:setParameter 可以融合哈?
113 113 /**
114 114 * 设置单个参数
115 115 *
... ... @@ -121,4 +121,5 @@ public class PluginScriptContext implements ScriptContext {
121 121 parameters.put(key, value);
122 122 return this;
123 123 }
  124 +
124 125 }
125 126 \ No newline at end of file
... ...
yudao-module-iot/yudao-module-iot-plugins/yudao-module-iot-plugin-script/src/main/java/cn/iocoder/yudao/module/iot/plugin/script/context/ScriptContext.java
... ... @@ -37,6 +37,7 @@ public interface ScriptContext {
37 37 */
38 38 Object getParameter(String key);
39 39  
  40 + // TODO @haohao:这个要不也是 setFunction
40 41 /**
41 42 * 注册函数
42 43 *
... ... @@ -44,4 +45,5 @@ public interface ScriptContext {
44 45 * @param function 函数对象
45 46 */
46 47 void registerFunction(String name, Object function);
47   -}
48 48 \ No newline at end of file
  49 +
  50 +}
49 51 \ No newline at end of file
... ...
yudao-module-iot/yudao-module-iot-plugins/yudao-module-iot-plugin-script/src/main/java/cn/iocoder/yudao/module/iot/plugin/script/engine/AbstractScriptEngine.java
... ... @@ -48,4 +48,4 @@ public abstract class AbstractScriptEngine {
48 48 public void setSandbox(ScriptSandbox sandbox) {
49 49 this.sandbox = sandbox;
50 50 }
51   -}
52 51 \ No newline at end of file
  52 +}
53 53 \ No newline at end of file
... ...
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
... ... @@ -12,8 +12,8 @@ import java.util.concurrent.Callable;
12 12 import java.util.concurrent.ConcurrentHashMap;
13 13  
14 14 /**
15   - * JavaScript脚本引擎实现
16   - * 使用JSR-223 Nashorn脚本引擎
  15 + * JavaScript 脚本引擎实现
  16 + * 使用 JSR-223 Nashorn 脚本引擎
17 17 */
18 18 @Slf4j
19 19 public class JsScriptEngine extends AbstractScriptEngine {
... ... @@ -24,7 +24,7 @@ public class JsScriptEngine extends AbstractScriptEngine {
24 24 private static final long DEFAULT_TIMEOUT_MS = 5000;
25 25  
26 26 /**
27   - * JavaScript引擎名称
  27 + * JavaScript 引擎名称
28 28 */
29 29 private static final String JS_ENGINE_NAME = "nashorn";
30 30  
... ... @@ -45,25 +45,24 @@ public class JsScriptEngine extends AbstractScriptEngine {
45 45  
46 46 @Override
47 47 public void init() {
48   - log.info("初始化JavaScript脚本引擎");
  48 + log.info("初始化 JavaScript 脚本引擎");
49 49  
50 50 // 创建脚本引擎管理器
51 51 engineManager = new ScriptEngineManager();
52 52  
53   - // 获取JavaScript引擎
  53 + // 获取 JavaScript 引擎
54 54 engine = engineManager.getEngineByName(JS_ENGINE_NAME);
55 55 if (engine == null) {
56   - log.error("无法创建JavaScript引擎,尝试使用JavaScript名称获取");
  56 + log.error("无法创建JavaScript引擎,尝试使用 JavaScript 名称获取");
57 57 engine = engineManager.getEngineByName("JavaScript");
58 58 }
59   -
60 59 if (engine == null) {
61   - throw new IllegalStateException("无法创建JavaScript引擎,请检查环境配置");
  60 + throw new IllegalStateException("无法创建 JavaScript 引擎,请检查环境配置");
62 61 }
63 62  
64 63 log.info("成功创建JavaScript引擎: {}", engine.getClass().getName());
65 64  
66   - // 默认使用JS沙箱
  65 + // 默认使用 JS 沙箱
67 66 if (sandbox == null) {
68 67 setSandbox(new JsSandbox());
69 68 }
... ... @@ -99,7 +98,7 @@ public class JsScriptEngine extends AbstractScriptEngine {
99 98 // 执行脚本
100 99 return engine.eval(script, bindings);
101 100 } catch (ScriptException e) {
102   - log.error("执行JavaScript脚本异常: {}", e.getMessage());
  101 + log.error("执行 JavaScript 脚本异常: {}", e.getMessage());
103 102 throw new RuntimeException("脚本执行异常: " + e.getMessage(), e);
104 103 }
105 104 };
... ... @@ -136,7 +135,7 @@ public class JsScriptEngine extends AbstractScriptEngine {
136 135 // 执行脚本
137 136 return engine.eval(script, bindings);
138 137 } catch (ScriptException e) {
139   - log.error("执行JavaScript脚本异常: {}", e.getMessage());
  138 + log.error("执行 JavaScript 脚本异常: {}", e.getMessage());
140 139 throw new RuntimeException("脚本执行异常: " + e.getMessage(), e);
141 140 }
142 141 };
... ... @@ -152,9 +151,10 @@ public class JsScriptEngine extends AbstractScriptEngine {
152 151  
153 152 @Override
154 153 public void destroy() {
155   - log.info("销毁JavaScript脚本引擎");
  154 + log.info("销毁 JavaScript 脚本引擎");
156 155 cachedScripts.clear();
157 156 engine = null;
158 157 engineManager = null;
159 158 }
  159 +
160 160 }
161 161 \ No newline at end of file
... ...
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 1 package cn.iocoder.yudao.module.iot.plugin.script.engine;
2 2  
  3 +import cn.hutool.core.lang.Assert;
3 4 import lombok.extern.slf4j.Slf4j;
4 5 import org.springframework.stereotype.Component;
5 6  
... ... @@ -11,12 +12,12 @@ import org.springframework.stereotype.Component;
11 12 public class ScriptEngineFactory {
12 13  
13 14 /**
14   - * 创建JavaScript脚本引擎
  15 + * 创建 JavaScript 脚本引擎
15 16 *
16 17 * @return JavaScript脚本引擎
17 18 */
18 19 public JsScriptEngine createJsEngine() {
19   - log.debug("创建JavaScript脚本引擎");
  20 + log.debug("创建 JavaScript 脚本引擎");
20 21 return new JsScriptEngine();
21 22 }
22 23  
... ... @@ -27,10 +28,7 @@ public class ScriptEngineFactory {
27 28 * @return 脚本引擎
28 29 */
29 30 public AbstractScriptEngine createEngine(String scriptType) {
30   - if (scriptType == null || scriptType.isEmpty()) {
31   - throw new IllegalArgumentException("脚本类型不能为空");
32   - }
33   -
  31 + Assert.notBlank(scriptType, "脚本类型不能为空");
34 32 switch (scriptType.toLowerCase()) {
35 33 case "js":
36 34 case "javascript":
... ... @@ -40,4 +38,5 @@ public class ScriptEngineFactory {
40 38 throw new IllegalArgumentException("不支持的脚本类型: " + scriptType);
41 39 }
42 40 }
43   -}
44 41 \ No newline at end of file
  42 +
  43 +}
45 44 \ No newline at end of file
... ...
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 1 package cn.iocoder.yudao.module.iot.plugin.script.sandbox;
2 2  
  3 +import cn.hutool.core.util.StrUtil;
3 4 import lombok.extern.slf4j.Slf4j;
4 5  
5 6 import javax.script.ScriptEngine;
... ... @@ -8,8 +9,9 @@ import java.util.HashSet;
8 9 import java.util.Set;
9 10 import java.util.regex.Pattern;
10 11  
  12 +// TODO @haohao:这个是不是融合到 ScriptEngine 里
11 13 /**
12   - * JavaScript脚本沙箱,限制脚本的执行权限
  14 + * JavaScript 脚本沙箱,限制脚本的执行权限
13 15 */
14 16 @Slf4j
15 17 public class JsSandbox implements ScriptSandbox {
... ... @@ -34,6 +36,7 @@ public class JsSandbox implements ScriptSandbox {
34 36 "(?:\\bchild_process\\b)|" +
35 37 "(?:\\bwindow\\b)");
36 38  
  39 + // TODO @haohao:这个没用到哈。
37 40 /**
38 41 * 脚本执行超时时间(毫秒)
39 42 */
... ... @@ -44,30 +47,28 @@ public class JsSandbox implements ScriptSandbox {
44 47 if (!(engineContext instanceof ScriptEngine)) {
45 48 throw new IllegalArgumentException("引擎上下文类型不正确,无法应用JavaScript沙箱");
46 49 }
47   -
48 50 ScriptEngine engine = (ScriptEngine) engineContext;
49 51  
50   - // 在Nashorn引擎中,可以通过以下方式设置安全限制
  52 + // 在 Nashorn 引擎中,可以通过以下方式设置安全限制
51 53 try {
52 54 // 设置严格模式
53 55 String securityPrefix = "'use strict';\n";
54 56  
55   - // 禁用Java.type等访问系统资源的功能
  57 + // 禁用 Java.type 等访问系统资源的功能
56 58 engine.eval("var Java = undefined;");
57 59 engine.eval("var JavaImporter = undefined;");
58 60 engine.eval("var Packages = undefined;");
59 61  
60 62 // 增强安全控制可以在这里添加
61   - log.debug("已应用JavaScript安全沙箱限制");
62   -
  63 + log.debug("已应用 JavaScript 安全沙箱限制");
63 64 } catch (Exception e) {
64   - log.warn("应用JavaScript沙箱限制失败: {}", e.getMessage());
  65 + log.warn("应用 JavaScript 沙箱限制失败: {}", e.getMessage());
65 66 }
66 67 }
67 68  
68 69 @Override
69 70 public boolean validateScript(String script) {
70   - if (script == null || script.isEmpty()) {
  71 + if (StrUtil.isNotEmpty(script)) {
71 72 return false;
72 73 }
73 74  
... ... @@ -86,11 +87,12 @@ public class JsSandbox implements ScriptSandbox {
86 87 }
87 88  
88 89 // 脚本长度限制
89   - if (script.length() > 1024 * 100) { // 限制100KB
  90 + if (script.length() > 1024 * 100) { // 限制 100 KB
90 91 log.warn("脚本太大,超过了限制");
91 92 return false;
92 93 }
93 94  
94 95 return true;
95 96 }
  97 +
96 98 }
97 99 \ No newline at end of file
... ...
yudao-module-iot/yudao-module-iot-plugins/yudao-module-iot-plugin-script/src/main/java/cn/iocoder/yudao/module/iot/plugin/script/sandbox/ScriptSandbox.java
... ... @@ -20,4 +20,5 @@ public interface ScriptSandbox {
20 20 * @return 是否安全
21 21 */
22 22 boolean validateScript(String script);
23   -}
24 23 \ No newline at end of file
  24 +
  25 +}
25 26 \ No newline at end of file
... ...
yudao-module-iot/yudao-module-iot-plugins/yudao-module-iot-plugin-script/src/main/java/cn/iocoder/yudao/module/iot/plugin/script/service/ScriptService.java
... ... @@ -12,7 +12,7 @@ public interface ScriptService {
12 12 /**
13 13 * 执行脚本
14 14 *
15   - * @param scriptType 脚本类型(如js、groovy等)
  15 + * @param scriptType 脚本类型(如 js、groovy 等)
16 16 * @param script 脚本内容
17 17 * @param context 脚本上下文
18 18 * @return 脚本执行结果
... ... @@ -22,7 +22,7 @@ public interface ScriptService {
22 22 /**
23 23 * 执行脚本
24 24 *
25   - * @param scriptType 脚本类型(如js、groovy等)
  25 + * @param scriptType 脚本类型(如 js、groovy 等)
26 26 * @param script 脚本内容
27 27 * @param params 脚本参数
28 28 * @return 脚本执行结果
... ... @@ -30,7 +30,7 @@ public interface ScriptService {
30 30 Object executeScript(String scriptType, String script, Map<String, Object> params);
31 31  
32 32 /**
33   - * 执行JavaScript脚本
  33 + * 执行 JavaScript 脚本
34 34 *
35 35 * @param script 脚本内容
36 36 * @param context 脚本上下文
... ... @@ -39,7 +39,7 @@ public interface ScriptService {
39 39 Object executeJavaScript(String script, ScriptContext context);
40 40  
41 41 /**
42   - * 执行JavaScript脚本
  42 + * 执行 JavaScript 脚本
43 43 *
44 44 * @param script 脚本内容
45 45 * @param params 脚本参数
... ... @@ -55,4 +55,5 @@ public interface ScriptService {
55 55 * @return 脚本是否安全
56 56 */
57 57 boolean validateScript(String scriptType, String script);
58   -}
59 58 \ No newline at end of file
  59 +
  60 +}
60 61 \ No newline at end of file
... ...
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
... ... @@ -38,6 +38,7 @@ public class ScriptServiceImpl implements ScriptService {
38 38 @PostConstruct
39 39 public void init() {
40 40 // 初始化常用的脚本引擎和沙箱
  41 + // TODO @haohao:js 是不是要枚举下哈。
41 42 getEngine("js");
42 43 sandboxCache.put("js", new JsSandbox());
43 44 }
... ... @@ -49,6 +50,7 @@ public class ScriptServiceImpl implements ScriptService {
49 50 try {
50 51 engine.destroy();
51 52 } catch (Exception e) {
  53 + // TODO @haohao:engine 类名
52 54 log.error("销毁脚本引擎失败", e);
53 55 }
54 56 }
... ... @@ -58,6 +60,7 @@ public class ScriptServiceImpl implements ScriptService {
58 60  
59 61 @Override
60 62 public Object executeScript(String scriptType, String script, ScriptContext context) {
  63 + // TODO @haohao:可以使用 hutool assert
61 64 if (scriptType == null || script == null) {
62 65 throw new IllegalArgumentException("脚本类型和内容不能为空");
63 66 }
... ... @@ -74,6 +77,7 @@ public class ScriptServiceImpl implements ScriptService {
74 77 // 执行脚本
75 78 return engine.execute(script, context);
76 79 } catch (Exception e) {
  80 + // TODO @haohao:最好把 e 堆栈出来哈;然后,engine 类名
77 81 log.error("执行脚本失败: {}", e.getMessage());
78 82 throw new RuntimeException("执行脚本失败: " + e.getMessage(), e);
79 83 }
... ... @@ -83,16 +87,19 @@ public class ScriptServiceImpl implements ScriptService {
83 87 public Object executeScript(String scriptType, String script, Map<String, Object> params) {
84 88 // 创建默认上下文
85 89 ScriptContext context = new PluginScriptContext(params);
  90 + // 执行脚本
86 91 return executeScript(scriptType, script, context);
87 92 }
88 93  
89 94 @Override
90 95 public Object executeJavaScript(String script, ScriptContext context) {
  96 + // TODO @haohao:枚举哈
91 97 return executeScript("js", script, context);
92 98 }
93 99  
94 100 @Override
95 101 public Object executeJavaScript(String script, Map<String, Object> params) {
  102 + // TODO @haohao:枚举哈
96 103 return executeScript("js", script, params);
97 104 }
98 105  
... ... @@ -100,7 +107,8 @@ public class ScriptServiceImpl implements ScriptService {
100 107 public boolean validateScript(String scriptType, String script) {
101 108 ScriptSandbox sandbox = sandboxCache.get(scriptType.toLowerCase());
102 109 if (sandbox == null) {
103   - log.warn("找不到脚本类型[{}]对应的沙箱,使用默认JS沙箱", scriptType);
  110 + // TODO @haohao:疑问,为啥默认 JsSandbox 哈?
  111 + log.warn("[validateScript][找不到脚本类型[{}]对应的沙箱,使用默认 JS 沙箱]", scriptType);
104 112 sandbox = new JsSandbox();
105 113 sandboxCache.put(scriptType.toLowerCase(), sandbox);
106 114 }
... ... @@ -120,4 +128,4 @@ public class ScriptServiceImpl implements ScriptService {
120 128 return engine;
121 129 });
122 130 }
123   -}
124 131 \ No newline at end of file
  132 +}
125 133 \ No newline at end of file
... ...
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
... ... @@ -6,6 +6,9 @@ import lombok.extern.slf4j.Slf4j;
6 6 import java.util.Map;
7 7 import java.util.concurrent.*;
8 8  
  9 +// TODO @haohao:【重要】 ScriptUtil.createGroovyEngine() 可以服用 hutool 的封装么?
  10 +// TODO @haohao:【重要】 js 引擎,可能要看下 jdk8 的兼容性;
  11 +// TODO @haohao:【重要】我们要不 script 配置的时候,支持 scriptType?!感觉会更通用一些???groovy、python、js
9 12 /**
10 13 * 脚本工具类,提供执行脚本的辅助方法
11 14 */
... ... @@ -66,6 +69,7 @@ public class ScriptUtils {
66 69 * 关闭工具类的线程池
67 70 */
68 71 public static void shutdown() {
  72 + // TODO @芋艿:有没默认工具类,可以 shutdown
69 73 SCRIPT_EXECUTOR.shutdown();
70 74 try {
71 75 if (!SCRIPT_EXECUTOR.awaitTermination(10, TimeUnit.SECONDS)) {
... ... @@ -77,8 +81,9 @@ public class ScriptUtils {
77 81 }
78 82 }
79 83  
  84 + // TODO @芋艿:要不要使用 JsonUtils
80 85 /**
81   - * 将JSON字符串转换为Map
  86 + * 将 JSON 字符串转换为 Map
82 87 *
83 88 * @param json JSON字符串
84 89 * @return Map对象,转换失败则返回null
... ... @@ -86,19 +91,20 @@ public class ScriptUtils {
86 91 @SuppressWarnings("unchecked")
87 92 public static Map<String, Object> parseJson(String json) {
88 93 try {
89   - // 使用hutool的JSONUtil工具类解析JSON
90 94 return JSONUtil.toBean(json, Map.class);
91 95 } catch (Exception e) {
92   - log.error("解析JSON失败: {}", e.getMessage());
  96 + // TODO @haohao:json、e 都打印出来哈
  97 + log.error("[parseJson][解析JSON失败: {}]", e.getMessage());
93 98 return null;
94 99 }
95 100 }
96 101  
  102 + // TODO @芋艿:要不要封装成 utils
97 103 /**
98 104 * 尝试将对象转换为整数
99 105 *
100 106 * @param obj 需要转换的对象
101   - * @return 转换后的整数,如果无法转换则返回null
  107 + * @return 转换后的整数,如果无法转换则返回 null
102 108 */
103 109 public static Integer toInteger(Object obj) {
104 110 if (obj == null) {
... ... @@ -122,6 +128,7 @@ public class ScriptUtils {
122 128 return null;
123 129 }
124 130  
  131 + // TODO @芋艿:要不要封装成 utils
125 132 /**
126 133 * 尝试将对象转换为双精度浮点数
127 134 *
... ... @@ -158,10 +165,12 @@ public class ScriptUtils {
158 165 * @return 如果两个数值相等则返回true,否则返回false
159 166 */
160 167 public static boolean numbersEqual(Number a, Number b) {
  168 + // TODO @haohao:NumberUtil.equals(1, 1D)
161 169 if (a == null || b == null) {
162 170 return a == b;
163 171 }
164 172  
165 173 return Math.abs(a.doubleValue() - b.doubleValue()) < 0.0000001;
166 174 }
  175 +
167 176 }
168 177 \ No newline at end of file
... ...