@Slf4j
public class RuleCalcUtils {
public static String json = "{\"id\":1,\"parentId\":0,\"rules\":1,\"map\":[{\"id\":0.5752577160621279,\"parentId\":1,\"rules\":0,\"map\":[{\"id\":0.3720240970679103,\"parentId\":0.5752577160621279,\"rules\":1,\"map\":[{\"id\":0.9482223038757285,\"parentId\":0.3720240970679103,\"rules\":2,\"map\":[{\"userLevelParam\":\"司龄(月)\",\"range\":\"实时数据\",\"relation\":\"<\",\"userLevelValue\":\"20\"}],\"active\":false,\"level\":4,\"parentLevel\":3},{\"id\":0.7664580817231594,\"parentId\":0.3720240970679103,\"rules\":2,\"map\":[{\"userLevelParam\":\"案加分\",\"range\":\"实时数据\",\"relation\":\"==\",\"userLevelValue\":\"10\"}],\"active\":false,\"level\":4,\"parentLevel\":3},{\"id\":0.2566599589893268,\"parentId\":0.3720240970679103,\"rules\":2,\"map\":[{\"userLevelParam\":\"职类\",\"range\":\"实时数据\",\"relation\":\"属于\",\"userLevelValue\":\"哈哈\"}],\"active\":false,\"level\":4,\"parentLevel\":3}],\"active\":false,\"level\":3,\"parentLevel\":2},{\"id\":0.6148288892102602,\"parentId\":0.5752577160621279,\"rules\":2,\"map\":[{\"userLevelParam\":\"职类\",\"range\":\"实时数据\",\"relation\":\"属于\",\"userLevelValue\":\"职类\"}],\"active\":false,\"level\":3,\"parentLevel\":2}],\"active\":false,\"level\":2,\"parentLevel\":1},{\"id\":0.9406835531825224,\"parentId\":1,\"rules\":2,\"map\":[{\"userLevelParam\":\"司龄(月)\",\"range\":\"实时数据\",\"relation\":\"==\",\"userLevelValue\":\"10\"}],\"active\":false,\"level\":2,\"parentLevel\":1},{\"id\":0.3356643339471186,\"parentId\":1,\"rules\":2,\"map\":[{\"userLevelParam\":\"职务\",\"range\":\"实时数据\",\"relation\":\"属于\",\"userLevelValue\":\"职务\"}],\"active\":false,\"level\":2,\"parentLevel\":1},{\"id\":0.31367304398725127,\"parentId\":1,\"rules\":2,\"map\":[{\"userLevelParam\":\"职级\",\"range\":\"实时数据\",\"relation\":\"属于\",\"userLevelValue\":\"哈哈\"}],\"active\":false,\"level\":2,\"parentLevel\":1}],\"active\":false,\"level\":1,\"parentLevel\":0}";
private final static Integer RULE_LOGICAL_RELATION = 2;
/**
* 固定删除后4个字符的原因是因为处理最后拼接的多余逻辑运算符号,
* 4个字符是因代码块美观 strRule.append(" || ")加入2个空格,若不需要空格要删除数据为 2
*/
private final static Integer RULE_STRING_DISPOSE_LENGTH = 4;
static ScriptEngine jse = new ScriptEngineManager().getEngineByName("JavaScript");
public static void main(String[] args) throws Exception {
/**
* TODO exampleSource数据要在calcRule方法中取样本数据使用。可根据业务自行进行修改
*/
Map<String,String> exampleSource = new HashMap<>();
exampleSource.put("司龄(月)","10");
exampleSource.put("职级","职级");
exampleSource.put("职类","职类");
exampleSource.put("薯片分","10");
exampleSource.put("案加分","10");
exampleSource.put("WI分","10");
exampleSource.put("职务","职务");
boolean result = decisionMaking(json,exampleSource);
log.info("result calc {}",result);
}
public static boolean decisionMaking(String ruleStr,Map<String,String> exampleSource) throws Exception {
try {
List<JSONObject> rulesArr = new ArrayList<>();
StringBuilder strRule = new StringBuilder();
/*格式数据*/
JSONObject jsonStr = JSONObject.parseObject(ruleStr);
/*树形规则钻取*/
factorial(jsonStr.getJSONArray("map"), rulesArr, jsonStr.getString("rules"));
/*按照父类ID进行分组*/
Map<Object, List<JSONObject>> groupCalcMap = rulesArr.stream().collect(Collectors.groupingBy(info -> info.getString("parentId"),Collectors.toList()));
/*计算规则公式*/
calcRuleFormula(jsonStr.getString("id"),groupCalcMap,strRule,jsonStr.getInteger("rules"),exampleSource);
/*处理尾部多余运算符,规则参看常量(RULE_STRING_DISPOSE_LENGTH)描述*/
String resultStr = strRule.delete(strRule.length()-RULE_STRING_DISPOSE_LENGTH,strRule.length()).toString();
System.out.println(resultStr);
return (boolean) jse.eval(resultStr);
} catch (Exception e) {
throw new Exception("Rule calculation exception ! ",e);
}
}
/**
* 计算规则公式
* /*0(&&) 1(||) 2(end loop))
*/
private static void calcRuleFormula(String masterId,Map<Object, List<JSONObject>> groupCalcMap,StringBuilder strRule,Integer logicalRelationStr,Map<String,String> exampleSource){
if(!groupCalcMap.containsKey(masterId) || logicalRelationStr == RULE_LOGICAL_RELATION){
log.warn("Data exception parent rules different. masterId = {} ; rules info = {} ; rule_logical_relation = {}",masterId,groupCalcMap,logicalRelationStr);
return;
}
strRule.append("(");
for (JSONObject object : groupCalcMap.get(masterId)) {
/**
* 判断是否有逻辑运算符
*/
if(object.containsKey("relation")){
/*TODO 需要修改 按照逻辑修改 go */
//样本数据获取逻辑
if("司龄(月)".equals(object.getString("userLevelParam"))){
strRule.append(exampleSource.get("司龄(月)"));
}
if("职级".equals(object.getString("userLevelParam"))){
strRule.append("\'");
strRule.append(exampleSource.get("职级"));
strRule.append("\'");
}
if("职类".equals(object.getString("userLevelParam"))){
strRule.append("\'");
strRule.append(exampleSource.get("职类"));
strRule.append("\'");
}
if("薯片分".equals(object.getString("userLevelParam"))){
strRule.append(exampleSource.get("薯片分"));
}
if("案加分".equals(object.getString("userLevelParam"))){
strRule.append(exampleSource.get("案加分"));
}
if("WI分".equals(object.getString("userLevelParam"))){
strRule.append(exampleSource.get("WI分"));
}
if("职务".equals(object.getString("userLevelParam"))){
strRule.append("\'");
strRule.append(exampleSource.get("职务"));
strRule.append("\'");
}
/*运算规则替换*/
if(object.getString("relation").equals("属于")){
strRule.append("==");
strRule.append("\'");
strRule.append(object.getString("userLevelValue"));
strRule.append("\'");
}
else if (object.getString("relation").equals("不属于")){
strRule.append("!=");
strRule.append("\'");
strRule.append(object.getString("userLevelValue"));
strRule.append("\'");
}
else{
strRule.append(object.getString("relation"));
strRule.append(object.getString("userLevelValue"));
}
/*需修改 按照逻辑修改 end */
/**
*判断逻辑运算公式拼接
*/
if (object.getInteger("gradeStr") == BigDecimal.ZERO.intValue()) {
strRule.append(" && ");
} else {
strRule.append(" || ");
}
}else{
calcRuleFormula(object.getString("id"),groupCalcMap,strRule,object.getInteger("gradeStr"),exampleSource);
}
}
/**
*处理字符串拼接尾行多余运算符
*/
strRule.delete(strRule.length()-RULE_STRING_DISPOSE_LENGTH,strRule.length());
strRule.append(")");
if (logicalRelationStr == BigDecimal.ZERO.intValue()) {
strRule.append(" && ");
} else {
strRule.append(" || ");
}
}
/**
* 递归树形规则
* 钻取规则数据 0(&&) 1(||) 2(end loop(recursion)))
* gradeStr取值父级的rules,标识字迹规则的关系
*/
private static void factorial(JSONArray obj, List resultList, String gradeStr) {
for (Object o : obj) {
JSONObject subsets = JSONObject.parseObject(o.toString());
/**
*若还有子集继续递归
*/
if (subsets.getInteger("rules") == RULE_LOGICAL_RELATION) {
JSONArray arrayInfo = subsets.getJSONArray("map");
for (Object o1 : arrayInfo) {
JSONObject subsetInfo = JSONObject.parseObject(o1.toString());
subsetInfo.put("parentId", subsets.getString("parentId"));
subsetInfo.put("id", subsets.getString("id"));
subsetInfo.put("gradeStr", gradeStr);
resultList.add(JSONObject.parseObject(subsetInfo.toString()));
}
} else {
JSONObject subsetInfo = new JSONObject();
subsetInfo.put("parentId", subsets.getString("parentId"));
subsetInfo.put("id", subsets.getString("id"));
subsetInfo.put("gradeStr", gradeStr);
resultList.add(JSONObject.parseObject(subsetInfo.toString()));
/**
* 若不是结束标志继续递归钻取
*/
factorial(subsets.getJSONArray("map"), resultList, subsets.getString("rules"));
}
}
}
}
最后打印计算公式结果:
公式;true表示该规则匹配
(((10<20 || 10==10 || '职类'=='哈哈') && '职类'=='职类') || 10==10 || '职务'=='职务' || '职级'=='哈哈')
15:42:48.012 [main] INFO net.crisps.cloud.common.utils.RuleCalcUtils - result calc true