avatar

十六小站

欢迎来到我的个人主页! 期待与您分享我的经验与故事,一起探索技术的无穷可能!

  • 首页
  • NAS专题
  • 关于
Home 简单规则引擎
文章

简单规则引擎

Posted 2025-04-2 Updated 2025-04- 2
By 十六
29~37 min read

背景

在一些项目中我们需要通过用户自定义配置一些阈值逻辑进行判断是否触发阈值,例如大部分IOT项目中需要设置自动触发条件(例如:每天22:00后 且 窗帘关闭的情况下关灯)。

当然有一些成熟如规则引擎如:Drools和LiteFlow,但是这里写一款简单的java实现方案作为记录。

前端示例

以下示例实现了在贫困帮扶人员中的逻辑 (人员年龄大于80或者小于10岁,并且个人年收入小于1万或者家庭年收入少于3万的人员)

前端代码

前端使用的是elementui,这里可以根据自己的需求封装成组件。

<template>
  <div class="logic-builder">
    <el-card shadow="hover">
      <div slot="header" >
        <slot name="title"></slot>
      </div>

      <div style="height: 500px;overflow-y: auto" >
      <div class="logic-group" v-for="(group, groupIndex) in logicGroups" :key="groupIndex">
        <div class="group-header">
          <span>
            条件组 {{ groupIndex + 1 }}
            <el-select v-model="group.logic"  size="small" style="width: 100px">
              <el-option label="并且" value="AND"></el-option>
              <el-option label="或者" value="OR"></el-option>
            </el-select>
          </span>
          <el-button
            size="mini"
            type="danger"
            icon="el-icon-delete"
            circle
            @click="removeGroup(groupIndex)"
            v-if="logicGroups.length  > 1"
          ></el-button>
        </div>

        <div class="condition-item" v-for="(condition, condIndex) in group.conditions"  :key="condIndex">
          <el-input
            v-model="condition.field"
            placeholder="属性名"
            size="small"
            style="width: 120px"
          ></el-input>

          <el-select v-model="condition.operator"  size="small" style="width: 100px">
            <el-option label="大于" value=">"></el-option>
            <el-option label="小于" value="<"></el-option>
            <el-option label="等于" value="=="></el-option>
            <el-option label="不等于" value="!="></el-option>
            <el-option label="大于等于" value=">="></el-option>
            <el-option label="小于等于" value="<="></el-option>
          </el-select>

          <el-input-number
            v-model="condition.value"
            size="small"
            controls-position="right"
            style="width: 120px"
          ></el-input-number>

          <el-button
            size="mini"
            type="danger"
            icon="el-icon-delete"
            circle
            @click="removeCondition(groupIndex, condIndex)"
            v-if="group.conditions.length  > 1"
          ></el-button>
        </div>

        <el-button
          size="mini"
          type="primary"
          plain
          icon="el-icon-plus"
          @click="addCondition(groupIndex)"
        >添加条件</el-button>

        <div class="group-relation" v-if="groupIndex < logicGroups.length  - 1">
          <el-select v-model="group.relationToNext"  size="small" style="width: 100px">
            <el-option label="并且" value="AND"></el-option>
            <el-option label="或者" value="OR"></el-option>
          </el-select>
        </div>
      </div>
      </div>

      <div class="actions">
        <el-button size="mini" type="primary" icon="el-icon-plus" @click="addGroup('AND')">添加【并且】组</el-button>
        <el-button size="mini" type="warning" icon="el-icon-plus" @click="addGroup('OR')">添加【或者】组</el-button>
        <el-button size="mini" type="success" icon="el-icon-check" @click="saveLogic">保存规则</el-button>
      </div>
    </el-card>

    <el-card shadow="hover" class="preview-card">
      <div slot="header">
        <span>规则预览</span>
      </div>
      <pre>{{ logicPreview }}</pre>
    </el-card>
  </div>
</template>

<script>
export default {
  name: "LogicSelect",
  props: {
    fields: {
      type: Array,
      default: ()=>{ return [];}
    }
  },
  data() {
    return {
      logicGroups: [
        {
          logic: 'AND',
          conditions: [
            {
              field: '',
              operator: '>',
              value: 100
            }
          ],
          relationToNext: 'AND'
        }
      ]
    }
  },
  computed: {
    logicPreview() {
      let previewData = this.logicGroups.map(group => {
        const conditions = group.conditions.map(cond => {
          return `${cond.field}  ${cond.operator}  ${cond.value}`
        }).join(group.logic === "AND"?" && ":" || ");
        return `(${conditions}) ${group.relationToNext} `;
      }).join("").replaceAll("AND", "&&").replaceAll("OR", "||");
      return previewData.substring(0, previewData.length - 4)
    }
  },
  methods: {
    addGroup(logic) {
      this.logicGroups.push({
        logic,
        conditions: [
          {
            field: '',
            operator: '>',
            value: 0
          }
        ],
        relationToNext: 'AND'
      });
    },
    removeGroup(index) {
      this.logicGroups.splice(index, 1);
    },
    addCondition(groupIndex) {
      this.logicGroups[groupIndex].conditions.push({
        field: '',
        operator: '>',
        value: 0
      });
    },
    removeCondition(groupIndex, condIndex) {
      this.logicGroups[groupIndex].conditions.splice(condIndex, 1);
    },
    async saveLogic() {
      try {
        // 验证逻辑
        const isValid = this.validateLogic();
        if (!isValid) {
          this.$message.error(' 请填写完整的逻辑规则');
          return;
        }

        console.log(btoa(JSON.stringify(this.logicGroups))); return;
        // 发送到后端
        const response = await this.$http.post('/api/logic-rules', {
          logicGroups: this.logicGroups
        });

        this.$message.success(' 规则保存成功');
        console.log(' 保存的规则:', response.data);
      } catch (error) {
        console.error(' 保存规则失败:', error);
        this.$message.error(' 保存失败,请重试');
      }
    },
    validateLogic() {
      for (const group of this.logicGroups) {
        for (const cond of group.conditions) {
          if (!cond.field || cond.value === null || cond.value === undefined) {
            return false;
          }
        }
      }
      return true;
    }
  }
}
</script>

<style scoped>
.logic-builder {
  max-width: 800px;
  margin: 20px auto;
}

.logic-group {
  padding: 15px;
  margin-bottom: 15px;
  border: 1px solid #ebeef5;
  border-radius: 4px;
  background-color: #f5f7fa;
}

.group-header {
  display: flex;
  justify-content: space-between;
  align-items: center;
  margin-bottom: 10px;
  font-weight: bold;
}

.condition-item {
  display: flex;
  align-items: center;
  margin-bottom: 10px;
}

.condition-item > * {
  margin-right: 10px;
}

.group-relation {
  margin: 15px 0;
  text-align: center;
}

.actions {
  margin-top: 20px;
  text-align: center;
}

.preview-card {
  margin-top: 20px;
}

.preview-card pre {
  margin: 0;
  white-space: pre-wrap;
  word-wrap: break-word;
}

.logic-builder>>>.el-card__header{
  padding: 5px 20px;
}
</style>

后端示例(实体Bean)

import java.util.List;

// LogicRule.java
public class LogicRule {
    private Long id;
    private List<Condition> conditions;
    private String relationToNext; // "AND" 或 "OR"
    private String logic;

    // 嵌套Condition类
    public static class Condition {
        private String field;
        private String operator;
        private Double value;

        // 无参构造
        public Condition() {}

        // 全参构造
        public Condition(String field, String operator, Double value) {
            this.field  = field;
            this.operator  = operator;
            this.value  = value;
        }

        // getter和setter

        public String getField() { return field; }
        public void setField(String field) { this.field  = field; }
        public String getOperator() { return operator; }
        public void setOperator(String operator) { this.operator  = operator; }
        public Double getValue() { return value; }
        public void setValue(Double value) { this.value  = value; }
    }

    // getter和setter
    public Long getId() { return id; }
    public void setId(Long id) { this.id  = id; }
    public List<Condition> getConditions() { return conditions; }
    public void setConditions(List<Condition> conditions) { this.conditions  = conditions; }

    public String getRelationToNext() {
        return relationToNext;
    }

    public void setRelationToNext(String relationToNext) {
        this.relationToNext = relationToNext;
    }
    public String getLogic() {return logic;}
    public void setLogic(String logic) {this.logic = logic;}
}

后端示例(Service)

这里简单的将规则解析成json格式存储。

import com.alibaba.fastjson2.JSON;
import com.alibaba.fastjson2.JSONArray;
import com.ruoyi.warning.domain.LogicRule;
import com.ruoyi.warning.service.LogicRuleService;
import org.springframework.stereotype.Service;

import java.util.Iterator;
import java.util.Map;

@Service
public class LogicRuleServiceImpl {

    /**
     * 
     * @param warningThreshold 规则引擎JSON字符串
     * @param message  当前对象信息
     * @return
     */
    @Override
    public boolean isWarning(String warningThreshold, Map<String, Object> message){
        JSONArray jsonArray = JSON.parseArray(warningThreshold);
        Iterator<Object> groupIterator = jsonArray.iterator();
        LogicRule logicRule = JSON.parseObject(groupIterator.next().toString(), LogicRule.class);
        boolean result = evaluate(logicRule, message);
        String relation = logicRule.getRelationToNext();
        while (groupIterator.hasNext()) {
            LogicRule rule = JSON.parseObject(groupIterator.next().toString(), LogicRule.class);
            if ("AND".equals(relation)){
                result = result && evaluate(rule, message);
            } else if ("OR".equals(relation)){
                result = result || evaluate(rule, message);
            } else {
                throw new IllegalArgumentException("规则不存在");
            }
            relation = rule.getRelationToNext();
        }
        return result;
    }

    // 评估逻辑规则
    public boolean evaluate(LogicRule rule, Map<String, Object> params) {
        if (rule == null) {
            throw new IllegalArgumentException("规则不存在");
        }

        boolean result = "AND".equals(rule.getLogic());
        for (LogicRule.Condition condition : rule.getConditions())  {
            if (condition.getField() == null) {
                throw new IllegalArgumentException("缺少参数: " + condition.getField());
            }
            Double value = Double.parseDouble(params.get(condition.getField()).toString());

            boolean currentResult = evaluateCondition(value, condition);

            if ("AND".equals(rule.getLogic()))  {
                result = result && currentResult;
                if (!result) break; // AND短路优化
            } else {
                result = result || currentResult;
                if (result) break; // OR短路优化
            }
        }

        return result;
    }

    private boolean evaluateCondition(Double value, LogicRule.Condition condition) {
        switch (condition.getOperator())  {
            case ">": return value > condition.getValue();
            case "<": return value < condition.getValue();
            case "==": return Math.abs(value  - condition.getValue())  < 0.000001;
            case "!=": return Math.abs(value  - condition.getValue())  >= 0.000001;
            case ">=": return value >= condition.getValue();
            case "<=": return value <= condition.getValue();
            default: throw new IllegalArgumentException("不支持的运算符: " + condition.getOperator());
        }
    }

}

使用方案

  1. 数据存储,采用JSON格式存储。

  2. 前后端交互,因为涉及到特殊字符,推荐前端使用Base64进行编码后传入到后端,后端再进行解码实现对应的业务逻辑。

  3. 后端调用直接调用isWarning方法判断是否满足规则。

高手之路
前端 Java
License:  CC BY 4.0
Share

Further Reading

Apr 2, 2025

简单规则引擎

背景 在一些项目中我们需要通过用户自定义配置一些阈值逻辑进行判断是否触发阈值,例如大部分IOT项目中需要设置自动触发条件(例如:每天22:00后 且 窗帘关闭的情况下关灯)。 当然有一些成熟如规则引擎如:Drools和LiteFlow,但是这里写一款简单的java实现方案作为记录。 前端示例 以下示

Feb 24, 2025

Jvm参数

JVM参数的设置对应用程序的性能和稳定性至关重要,尤其是在高并发场景下。合理的JVM参数配置可以优化内存使用、减少GC停顿时间、提升吞吐量。合适的jvm参数能将服务器性能压榨到极致。 堆内存设置 堆内存是JVM中最重要的内存区域,用于存储对象实例。合理设置堆内存大小可以避免频繁GC和内存溢出。 -X

Dec 25, 2024

多线程的那些事

踩坑记录 在线程中打印的日志,没有错误信息和堆栈信息。 代码: ForkJoinTask<?> task = pool.submit(() -> { try { processContent(appendixs);

OLDER

在WEB中子线程可以访问Request上下文

NEWER

K6+Playwright实现并发测试

Recently Updated

  • Onlyoffice编译
  • K6+Playwright实现并发测试
  • 简单规则引擎
  • 在WEB中子线程可以访问Request上下文
  • onlyoffice配置

Trending Tags

Java Docker 前端 中间件 数据库 群晖 unraid

Contents

©2025 十六小站. 陕ICP备2023009742号-2