SpringBoot + 自定义 DSL + Groovy 脚本:构建可拖拽的业务规则配置平台

业务规则配置的痛点

在我们的日常开发中,经常会遇到这样的场景:

  • 产品经理:"这个活动规则要支持多种条件组合,用户可以根据需要自己配置"
  • 运营人员:"我想要配置一个促销规则:购买满100元且是VIP用户,再加购任意商品就送优惠券"
  • 技术人员:"又要改代码,这次需要重新发布..."

这种硬编码的方式不仅开发效率低,而且每次业务规则变化都需要技术人员参与,严重影响了业务响应速度。

解决方案思路

今天我们要解决的,就是如何构建一个可视化的业务规则配置平台,让业务人员也能自己配置复杂的业务规则。

核心思路是:

  1. 可视化配置:通过拖拽方式配置业务规则
  2. 自定义DSL:定义领域特定语言来表达业务逻辑
  3. Groovy脚本执行:运行时动态执行业务规则
  4. 规则验证:确保配置的规则语法正确、逻辑合理

技术选型

  • SpringBoot:快速搭建应用
  • Groovy:动态脚本执行
  • Vue.js:前端可视化配置界面
  • Jackson/Gson:JSON序列化
  • JSR-223 Script Engine:脚本引擎集成

核心实现思路

1. 自定义DSL设计

首先,我们需要设计一套简单易懂的DSL来表达业务规则:

// 示例DSL
rule "VIP用户满额优惠" {
    when
        user.level == "VIP" && order.amount >= 100
    then
        discount = order.amount * 0.1
        coupon = "COUPON_10_OFF"
}

2. 规则模型定义

定义规则的内部数据结构:

@Data
public class BusinessRule {
    private String id;
    private String name;
    private String description;
    private String dslScript;  // DSL脚本内容
    private List<RuleCondition> conditions;  // 条件列表
    private List<RuleAction> actions;        // 动作列表
    private boolean enabled;                 // 是否启用
    private int priority;                    // 执行优先级
}

@Data
public class RuleCondition {
    private String field;        // 字段名
    private String operator;     // 操作符:==, !=, >, <, contains等
    private Object value;        // 比较值
    private String logic;        // 逻辑连接符:AND, OR
}

3. DSL解析器

创建DSL解析器来将可视化配置转换为可执行的脚本:

@Component
public class DSLParser {
    
    public String parseToGroovy(BusinessRule rule) {
        StringBuilder script = new StringBuilder();
        
        // 构建条件部分
        script.append("if (");
        for (int i = 0; i < rule.getConditions().size(); i++) {
            RuleCondition condition = rule.getConditions().get(i);
            script.append(buildConditionExpression(condition));
            
            if (i < rule.getConditions().size() - 1) {
                script.append(" ").append(condition.getLogic()).append(" ");
            }
        }
        script.append(") {\n");
        
        // 构建动作部分
        for (RuleAction action : rule.getActions()) {
            script.append("    ").append(buildActionExpression(action)).append("\n");
        }
        
        script.append("}");
        return script.toString();
    }
    
    private String buildConditionExpression(RuleCondition condition) {
        String field = condition.getField();
        String operator = condition.getOperator();
        Object value = condition.getValue();
        
        switch (operator) {
            case "==":
                return field + " == '" + value + "'";
            case "!=":
                return field + " != '" + value + "'";
            case ">":
                return field + " > " + value;
            case "<":
                return field + " < " + value;
            case "contains":
                return field + ".contains('" + value + "')";
            default:
                throw new IllegalArgumentException("Unsupported operator: " + operator);
        }
    }
    
    private String buildActionExpression(RuleAction action) {
        return action.getTarget() + " = " + action.getValue();
    }
}

4. 规则执行引擎

创建规则执行引擎来运行Groovy脚本:

@Component
public class RuleEngine {
    
    private final ScriptEngine groovyScriptEngine;
    private final DSLParser dslParser;
    
    public RuleEngine(DSLParser dslParser) {
        this.dslParser = dslParser;
        this.groovyScriptEngine = new ScriptEngineManager().getEngineByName("groovy");
    }
    
    public RuleExecutionResult executeRule(BusinessRule rule, Map<String, Object> context) {
        try {
            // 解析DSL为Groovy脚本
            String groovyScript = dslParser.parseToGroovy(rule);
            
            // 创建脚本执行上下文
            Bindings bindings = groovyScriptEngine.createBindings();
            bindings.putAll(context);
            
            // 执行脚本
            Object result = groovyScriptEngine.eval(groovyScript, bindings);
            
            // 收集执行结果
            RuleExecutionResult executionResult = new RuleExecutionResult();
            executionResult.setSuccess(true);
            executionResult.setRuleId(rule.getId());
            executionResult.setRuleContext(new HashMap<>(bindings));
            
            return executionResult;
            
        } catch (Exception e) {
            log.error("规则执行失败: {}", rule.getName(), e);
            return RuleExecutionResult.failure(rule.getId(), e.getMessage());
        }
    }
    
    public List<RuleExecutionResult> executeRules(List<BusinessRule> rules, Map<String, Object> context) {
        // 按优先级排序执行规则
        return rules.stream()
                .sorted(Comparator.comparingInt(BusinessRule::getPriority))
                .map(rule -> executeRule(rule, context))
                .collect(Collectors.toList());
    }
}

5. 规则配置服务

提供完整的规则管理功能:

@Service
@Transactional
public class RuleManagementService {
    
    @Autowired
    private RuleEngine ruleEngine;
    
    @Autowired
    private DSLParser dslParser;
    
    /**
     * 验证规则语法
     */
    public ValidationResult validateRule(BusinessRule rule) {
        try {
            String groovyScript = dslParser.parseToGroovy(rule);
            // 尝试编译脚本以验证语法
            CompilerConfiguration config = new CompilerConfiguration();
            config.setOptimizationOptions(Collections.singletonMap("indy", false));
            GroovyShell shell = new GroovyShell(config);
            shell.parse(groovyScript);
            
            return ValidationResult.success();
        } catch (Exception e) {
            return ValidationResult.failure("规则语法错误: " + e.getMessage());
        }
    }
    
    /**
     * 测试规则执行
     */
    public RuleTestResult testRule(BusinessRule rule, Map<String, Object> testData) {
        return ruleEngine.executeRule(rule, testData);
    }
    
    /**
     * 保存规则
     */
    public void saveRule(BusinessRule rule) {
        // 验证规则
        ValidationResult validation = validateRule(rule);
        if (!validation.isSuccess()) {
            throw new BusinessException("规则验证失败: " + validation.getMessage());
        }
        
        // 保存到数据库
        ruleRepository.save(rule);
    }
    
    /**
     * 批量执行规则
     */
    public List<RuleExecutionResult> executeRulesForContext(String contextType, Map<String, Object> context) {
        // 根据上下文类型获取启用的规则
        List<BusinessRule> activeRules = ruleRepository.findByContextTypeAndEnabledTrue(contextType);
        
        return ruleEngine.executeRules(activeRules, context);
    }
}

6. REST API接口

提供前后端交互的API:

@RestController
@RequestMapping("/api/rules")
public class RuleController {
    
    @Autowired
    private RuleManagementService ruleService;
    
    /**
     * 获取规则列表
     */
    @GetMapping
    public Result<List<BusinessRule>> getRules(
            @RequestParam(required = false) String contextType,
            @RequestParam(defaultValue = "true") Boolean enabled) {
        
        List<BusinessRule> rules = ruleService.getRules(contextType, enabled);
        return Result.success(rules);
    }
    
    /**
     * 保存规则
     */
    @PostMapping
    public Result<String> saveRule(@RequestBody BusinessRule rule) {
        ruleService.saveRule(rule);
        return Result.success("规则保存成功");
    }
    
    /**
     * 验证规则
     */
    @PostMapping("/validate")
    public Result<ValidationResult> validateRule(@RequestBody BusinessRule rule) {
        ValidationResult result = ruleService.validateRule(rule);
        return Result.success(result);
    }
    
    /**
     * 测试规则
     */
    @PostMapping("/test")
    public Result<RuleTestResult> testRule(@RequestBody RuleTestRequest request) {
        RuleTestResult result = ruleService.testRule(request.getRule(), request.getTestData());
        return Result.success(result);
    }
    
    /**
     * 执行规则
     */
    @PostMapping("/execute")
    public Result<List<RuleExecutionResult>> executeRules(@RequestBody RuleExecutionContext context) {
        List<RuleExecutionResult> results = ruleService.executeRulesForContext(
            context.getContextType(), 
            context.getContextData()
        );
        return Result.success(results);
    }
}

前端可视化设计

前端主要包含以下几个组件:

  1. 规则画布:可拖拽的规则配置界面
  2. 条件配置面板:配置规则条件
  3. 动作配置面板:配置规则动作
  4. 预览面板:实时预览生成的DSL代码
  5. 测试面板:测试规则执行结果

前端可以通过拖拽组件来构建规则逻辑,然后实时生成相应的DSL脚本。

安全性考虑

在实际应用中,需要注意以下安全问题:

  1. 脚本沙箱:限制Groovy脚本的执行权限
  2. 输入验证:严格验证用户输入,防止恶意脚本注入
  3. 执行时间限制:设置脚本执行超时时间
  4. 资源限制:限制脚本使用的内存和CPU资源

优势分析

相比传统硬编码方式,这种方案的优势明显:

  1. 灵活性:业务人员可自主配置复杂规则,无需开发介入
  2. 可视化:拖拽式配置,降低使用门槛
  3. 可维护性:规则集中管理,便于维护和调试
  4. 扩展性:支持复杂的条件组合和业务逻辑
  5. 实时生效:规则修改后可立即生效,无需重启应用

注意事项

  1. 性能影响:动态脚本执行会有一定性能开销
  2. 调试困难:相比静态代码,动态脚本调试相对困难
  3. 安全性:需要严格的权限控制和安全验证
  4. 版本管理:需要对规则进行版本管理,支持回滚

总结

通过SpringBoot + 自定义DSL + Groovy脚本的技术组合,我们可以构建一个强大而灵活的业务规则配置平台。这不仅能大幅提升业务响应速度,还能解放开发人员,让他们专注于更有价值的技术工作。

在实际项目中,建议根据具体业务需求进行定制化开发,并充分考虑安全性、性能和可维护性等因素。


服务端技术精选,专注分享后端开发实战技术,助力你的技术成长!


标题:SpringBoot + 自定义 DSL + Groovy 脚本:构建可拖拽的业务规则配置平台
作者:jiangyi
地址:http://www.jiangyi.space/articles/2026/01/09/1767936645644.html

    0 评论
avatar