Java内嵌Groovy脚本引擎进行业务规则剥离(四)

Wesley13
• 阅读 329

前三篇文章已从groovy脚本的基本使用到java解析,json对象结合作了一基础铺垫。这篇文章是本系列的最后一篇文章。

在第二篇文章中,说到用groovy编写的规则脚本,然后采用文件+字符串的形式加载到java中,解析并运行。

在我的应用场景中,每一条规则就是一个业务指标项。每一个指标项能够单独维护,而不是把所有指标项统一放到一个文件中。

那么,我需要对规则按每一个指标项进行碎片化切分。

这些指标项一般而言,是存放在数据表中的,然后提供统一的配置界面进行调整维护。

上代案例代码:

package com.amarsoft.rax.rulengine;


import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;

import javax.script.Bindings;
import javax.script.ScriptEngine;
import javax.script.ScriptEngineManager;
import javax.script.ScriptException;

import com.amarsoft.rax.lang.DataObject;


public class DynamicGroovyScript {

    /**
     * 规则集以及每个指标项的计算脚本
     * @return
     */
    private DataObject getRuleset(){
        DataObject ruleSet = new DataObject();
        
        ruleSet.xput("规则集.基础信息.是否90后", "student.birth >= '1990/01/01'?'是':'否'");
        ruleSet.xput("规则集.基础信息.性别", "student.gender == 'M' ? '男' : '女'");
        ruleSet.xput("规则集.基础信息.注册天数", "to_date(today) - to_date(student.createTime)");
        ruleSet.xput("规则集.基础信息.地区", areaScript());
        ruleSet.xput("规则集.评级.学费档次", "tuitionFee>=60000?'A':(tuitionFee>=40000&&tuitionFee<60000?'B':(tuitionFee>=20000&&tuitionFee<40000)?'C':'D')");
        ruleSet.xput("规则集.评级.收入档次", incomeScript());
        ruleSet.xput("规则集.学习情况.总学时", totalHoursScript());
        ruleSet.xput("规则集.学习情况.迟到次数", delayTimes());
        ruleSet.xput("规则集.学习情况.出勤次数", "student.extra.attendanceLog.size()");
        ruleSet.xput("规则集.学习情况.单门课程最长学时", maxCourseHours());

        return ruleSet;
    }
    /**
     * 业务原始数据
     * @return
     */
    private DataObject getBusinessData(){
        DataObject businessData = new DataObject();
        
        businessData.xput("collegeName", "EMBA业余大学");
        businessData.xput("tuitionFee", 80000);
        businessData.xput("startDate", "2016/02/01");
        businessData.xput("finishDate", "2016/09/01");
        businessData.xput("today", "2017/01/21");
        //下一层级的学生对象
        businessData.xput("student.id", "E9527");
        businessData.xput("student.name", "于小小");
        businessData.xput("student.gender", "F");
        businessData.xput("student.kind", "EMBA");
        businessData.xput("student.className", "重庆理工大学MBA三年级四班");
        businessData.xput("student.grade", 4);
        businessData.xput("student.birth", "1989/03/02");
        businessData.xput("student.address", "重庆市巴南区红光大道");
        businessData.xput("student.salary", 50000);
        businessData.xput("student.createTime", "2016/01/21 11:31:00");
        businessData.xput("student.courses", getCourses());
        businessData.xput("student.mainInstructor", null);
        businessData.xput("student.extra.attendanceLog", getAttendanceLog());
        
        return businessData;
    }
    
    public static void main(String[] args) throws ScriptException{
        ScriptEngineManager engineManager = new ScriptEngineManager();
        ScriptEngine engine = engineManager.getEngineByName("groovy");
        Bindings variables = engine.createBindings();
        
        DynamicGroovyScript ins = new DynamicGroovyScript();
        DataObject businessData = ins.getBusinessData();
        variables.putAll(businessData);
        
        DataObject ruleSet = ins.getRuleset();
//        System.out.println(ruleSet.getJSONString());
        
//        System.out.println(engine.eval("student.birth >= '1990/01/01'?'是':'否'",variables));
        Iterator<String> iterator = ruleSet.xpathKeyIterator();
        while(iterator.hasNext()){
            String key = iterator.next();
            Object value = ruleSet.getObject(key);
            if(!(value instanceof String))continue;
            String script = (String)value;
            if(script == null||script.replaceAll("\\s+", "").length()==0)continue;
            engine.eval(ins.getGlobleScript(""));    //先执行下,把全局函数放进去
//            System.out.println(key+"="+value+" --->值:["+engine.eval(script,variables)+"]");
            System.out.println(key+"="+engine.eval(script,variables));
        }
    }
    
    private List<String> getAttendanceLog(){
        List<String> list = new ArrayList<String>();
        
        list.add("2016/02/01 09:00:02");
        list.add("2016/02/03 08:50:03");
        list.add("2016/02/15 09:12:34");
        list.add("2016/04/01 07:30:11");
        
        return list;
    }
    
    private DataObject[] getCourses(){
        DataObject[] courses = new  DataObject[]{
                new DataObject(),
                new DataObject(),
                new DataObject(),
                new DataObject(),
                new DataObject(),
                new DataObject(),
        };
        courses[0].xput("id", "GJC");
        courses[0].xput("name", "公共基础");
        courses[0].xput("classHour", 32);    
        
        courses[1].xput("id", "ZXW");
        courses[1].xput("name", "组织行为学");
        courses[1].xput("classHour", 40);    
        
        courses[2].xput("id", "TJX");
        courses[2].xput("name", "统计学");
        courses[2].xput("classHour", 20);    
        
        courses[3].xput("id", "CJR");
        courses[3].xput("name", "财务与金融");
        courses[3].xput("classHour", 60);    
        
        courses[4].xput("id", "JJF");
        courses[4].xput("name", "经济法");
        courses[4].xput("classHour", 48);    
        
        courses[5].xput("id", "JSJ");
        courses[5].xput("name", "计算机技能");
        courses[5].xput("classHour", 16);    
        return courses;
    }
    
    private String areaScript(){
        StringBuilder execScript = new StringBuilder();
        execScript.append("({").append("\n");
        execScript.append("    def province = student.address.subSequence(0,3)").append("\n");
        execScript.append("    def areaMapping = ['西南':['重庆市','四川省','贵州省','云南省'],'江浙沪':['上海市','江苏省','浙江省'],'京津冀':['北京市','天津市','河北省']]").append("\n");
        execScript.append("    def entry = areaMapping.find {key,value -> ").append("\n");
        execScript.append("        value.contains(province)").append("\n");
        execScript.append("    }").append("\n");
        execScript.append("    entry.key").append("\n");
        execScript.append("})()").append("\n");
        
        return execScript.toString();
    }
    private String incomeScript(){
        StringBuilder execScript = new StringBuilder();
        execScript.append("({").append("\n");
        execScript.append("    if(student.salary>=20000) '高收入'").append("\n");
        execScript.append("    else if(student.salary>=10000) '中等收入'").append("\n");
        execScript.append("    else if(student.salary>=5000) '一般收入'").append("\n");
        execScript.append("    else '低收入'").append("\n");
        execScript.append("})()").append("\n");
        
        return execScript.toString();
    }
    private String totalHoursScript(){
        StringBuilder execScript = new StringBuilder();
        execScript.append("({").append("\n");
        execScript.append("    int totalHourse = 0;").append("\n");
        execScript.append("    student.courses.each { totalHourse += it.classHour}").append("\n");
        execScript.append("    totalHourse").append("\n");
        execScript.append("})()").append("\n");
        
        return execScript.toString();
    }
    private String delayTimes(){
        StringBuilder execScript = new StringBuilder();
        execScript.append("({").append("\n");
        execScript.append("    int _count = 0").append("\n");
        execScript.append("    student.extra.attendanceLog.each {").append("\n");
        execScript.append("    Date _date = to_date(it)").append("\n");
        execScript.append("    _count += (_date.hours>=9&&_date.seconds>=1)?1:0").append("\n");
        execScript.append("    }").append("\n");
        execScript.append("    _count").append("\n");
        execScript.append("})()").append("\n");
        
        return execScript.toString();
    }
    private String maxCourseHours(){
        StringBuilder execScript = new StringBuilder();
        execScript.append("({").append("\n");
        execScript.append("    int maxHour = 0;").append("\n");
        execScript.append("    student.courses.each { maxHour = Math.max(maxHour,it.classHour)}").append("\n");
        execScript.append("    student.courses.find({it.classHour == maxHour}) //默认最后一句为返回值").append("\n");
        execScript.append("})()").append("\n");
        
        return execScript.toString();
    }
    
    
    public String getGlobleScript(String script){
        StringBuilder execScript = new StringBuilder();
        //增加一个日期处理方法
        execScript.append("import java.text.SimpleDateFormat").append("\n");
        execScript.append("import java.text.ParseException").append("\n");
        
        execScript.append("def to_date(_date){").append("\n");
        execScript.append("    try{").append("\n");
        execScript.append("        return (new SimpleDateFormat(\"yyyy/MM/dd hh:mm:ss\")).parse(_date);").append("\n");
        execScript.append("    }catch(ParseException e){").append("\n");
        execScript.append("        return (new SimpleDateFormat(\"yyyy/MM/dd\")).parse(_date);").append("\n");
        execScript.append("    }").append("\n");
        execScript.append("}").append("\n");
        
        execScript.append(script);
        
        return execScript.toString();
    }
    
}

运行效果如下图:

Java内嵌Groovy脚本引擎进行业务规则剥离(四)

点赞
收藏
评论区
推荐文章
blmius blmius
2年前
MySQL:[Err] 1292 - Incorrect datetime value: ‘0000-00-00 00:00:00‘ for column ‘CREATE_TIME‘ at row 1
文章目录问题用navicat导入数据时,报错:原因这是因为当前的MySQL不支持datetime为0的情况。解决修改sql\mode:sql\mode:SQLMode定义了MySQL应支持的SQL语法、数据校验等,这样可以更容易地在不同的环境中使用MySQL。全局s
Jacquelyn38 Jacquelyn38
2年前
2020年前端实用代码段,为你的工作保驾护航
有空的时候,自己总结了几个代码段,在开发中也经常使用,谢谢。1、使用解构获取json数据let jsonData  id: 1,status: "OK",data: 'a', 'b';let  id, status, data: number   jsonData;console.log(id, status, number )
皕杰报表之UUID
​在我们用皕杰报表工具设计填报报表时,如何在新增行里自动增加id呢?能新增整数排序id吗?目前可以在新增行里自动增加id,但只能用uuid函数增加UUID编码,不能新增整数排序id。uuid函数说明:获取一个UUID,可以在填报表中用来创建数据ID语法:uuid()或uuid(sep)参数说明:sep布尔值,生成的uuid中是否包含分隔符'',缺省为
御弟哥哥 御弟哥哥
3年前
Groovy 集合与闭包
Groovy集合在Groovy提供的所有方便的快捷方式和功能中,最有帮助的一个可能就是内置的集合。回想一下在Java编程中是如何使用集合的—导入java.util类,初始化集合,将项加入集合。这三个步骤都会增加不少代码。而Groovy可以直接在语言内使用集合。在Groovy中,不需要导入专门的类,也不需要初始化对象。集合是语
Wesley13 Wesley13
2年前
Java内嵌Groovy脚本引擎进行业务规则剥离(一)
一些常见商业应用程序或企业应用,大多都会遇上业务规则在一定的条件下,允许进行一些灵活的配置,以满足业务变化的需要。解决的方式大致有以下几个方面:1.最为传统的方式是java程序直接写死提供几个可调节的参数配置然后封装成为独立的业务模块组件,在增加参数或简单调整规则后,重新调上线。2.最为彻底的解决方式,引入商业化规则引擎,如iLog,国产的“
Wesley13 Wesley13
2年前
Java日期时间API系列31
  时间戳是指格林威治时间1970年01月01日00时00分00秒起至现在的总毫秒数,是所有时间的基础,其他时间可以通过时间戳转换得到。Java中本来已经有相关获取时间戳的方法,Java8后增加新的类Instant等专用于处理时间戳问题。 1获取时间戳的方法和性能对比1.1获取时间戳方法Java8以前
Wesley13 Wesley13
2年前
00:Java简单了解
浅谈Java之概述Java是SUN(StanfordUniversityNetwork),斯坦福大学网络公司)1995年推出的一门高级编程语言。Java是一种面向Internet的编程语言。随着Java技术在web方面的不断成熟,已经成为Web应用程序的首选开发语言。Java是简单易学,完全面向对象,安全可靠,与平台无关的编程语言。
Stella981 Stella981
2年前
Django中Admin中的一些参数配置
设置在列表中显示的字段,id为django模型默认的主键list_display('id','name','sex','profession','email','qq','phone','status','create_time')设置在列表可编辑字段list_editable
为什么mysql不推荐使用雪花ID作为主键
作者:毛辰飞背景在mysql中设计表的时候,mysql官方推荐不要使用uuid或者不连续不重复的雪花id(long形且唯一),而是推荐连续自增的主键id,官方的推荐是auto_increment,那么为什么不建议采用uuid,使用uuid究
Python进阶者 Python进阶者
4个月前
Excel中这日期老是出来00:00:00,怎么用Pandas把这个去除
大家好,我是皮皮。一、前言前几天在Python白银交流群【上海新年人】问了一个Pandas数据筛选的问题。问题如下:这日期老是出来00:00:00,怎么把这个去除。二、实现过程后来【论草莓如何成为冻干莓】给了一个思路和代码如下:pd.toexcel之前把这