项目作者: jimuyang

项目描述 :
基于Groovy的决策和规则引擎
高级语言: JavaScript
项目地址: git://github.com/jimuyang/rule-pile.git
创建时间: 2019-12-09T06:38:48Z
项目社区:https://github.com/jimuyang/rule-pile

开源协议:

下载


rule-pile

基于 Groovy 的决策和规则引擎

原理

基于 Java 运行时调用 Groovy 脚本的能力,可以让 Java 具备像脚本一样的灵活性。
核心代码:Java 执行 Groovy 脚本传参并获取结果

  1. /**
  2. * 生成新的groovyClass
  3. */
  4. private Class buildGroovyClass(String groovyScript) throws RuleException {
  5. // 每个class都new一个loader 便于垃圾回收
  6. GroovyClassLoader loader = new GroovyClassLoader();
  7. try {
  8. return loader.parseClass(groovyScript);
  9. } catch (Exception e) {
  10. throw new RuleException("groovy脚本解析class出错:" + e.getMessage());
  11. }
  12. }
  13. ···
  14. // 获取groovyObject
  15. GroovyObject go = (GroovyObject) clazz.newInstance();
  16. // 传参并调用脚本的main方法
  17. Object result = groovyObject.invokeMethod("main", input);

实现

有了基本原理后,其实问题被转变成了如何生成Groovy脚本?,理所当然的回答是:开发写呗。
这样的做法是可行的,但有几个问题:

  1. 开发累死
  2. 容易出错
  3. 无法感受现有的逻辑

这里的解决方案是: 图编辑器->生成逻辑流程图->解析为Groovy脚本->执行。可见即可得

图编辑器

图编辑器选择g6,准确来说选择开箱即用的ant-design-pro+GGEditor,前端要改的内容其实不多,为应对生成逻辑的需要,需要添加一些表单项即可

解析图和逻辑依赖

编辑器的输出是node数组和edge数组,就是图上的节点和线们。后台解析这段json后,生成逻辑树。之后对逻辑树进行遍历后,生成最后的Groovy脚本。
逻辑树的遍历:

  1. void recursiveTravelNodeMap(Node node) throws RuleParseException {
  2. switch (node.getCategory()) {
  3. case "start":
  4. ···
  5. case "end":
  6. ···
  7. case "logic":
  8. ···
  9. case "common":
  10. ···
  11. default:
  12. }
  13. // node的后续节点
  14. for (ConnectNode target : node.getTargets()) {
  15. ···
  16. if (BOOLEAN_TRUE.equalsIgnoreCase(stream) && BOOLEAN_TRUE.equalsIgnoreCase(valve)) {
  17. this.recursiveTravelNodeMap(target.getNode());
  18. } else {
  19. this.mainBuilder.append("if ((").append(stream).append(").equals(").append(valve).append(")) {");
  20. this.recursiveTravelNodeMap(target.getNode());
  21. this.mainBuilder.append("}\n");
  22. }
  23. }
  24. if (defaultTarget != null) {
  25. this.recursiveTravelNodeMap(defaultTarget.getNode());
  26. }
  27. }

为了复用现有逻辑,提供了一套逻辑依赖的解析:

  1. // 使用模版生成函数main方法 脚本入口
  2. StringBuilder resultScript = new StringBuilder(String.format(template,
  3. mainRule.getRuleCode(), this.buildFuncOptions(mainRule.getOptions(), true)));
  4. // 将依赖的规则函数体放入
  5. for (RuRuleDefinition dependency : dependencies) {
  6. this.expandResultScript(resultScript, dependency);
  7. }
  8. return resultScript.toString();

具体设计