滴滴开源:逻辑引擎 LogicEngine 架构原理

avatar
@滴滴出行

作者:XIAOJUSURVEY

导读

XIAOJUSURVEY是滴滴开源的一套调研管理系统,提供面向个人和企业的一站式产品级解决方案,用于构建各类问卷、考试、测评和复杂表单。在产品形态上,XIAOJUSURVEY包含了B端和C端的完整能力,在技术上,XIAOJUSURVEY是一个全栈、跨端的项目。

项目地址:github.com/didi/xiaoju…

官网:xiaojusurvey.didi.cn

在线平台:xiaojuwenjuan.com

快速试用:www.xiaojuwenjuan.com/render/LWpB…

LogicEngineXIAOJUSURVEY用于实现业务多种逻辑编排能力的逻辑引擎,本期我们将围绕LogicEngine的架构原理进行分享。【文章最后有一波福利】,祝各位大佬2025快乐。

背景

问卷逻辑编排用于实现面向不同受访者的定制问卷,以满足不同场景的采样方案。

基于此功能,可以根据不同条件设定题目/选项间的逻辑关系,例如选择某个题目的选项后,显示或跳过某些问题等,以下是简单的演示。

LogicEngine

在实现上,需要考虑用户不同的操作、题目怎么配置相应目标题显隐逻辑等。基于传统的业务含义设定的配置驱动开发虽然也能满足需求,但在应对需求变化时往往需要大量的手动配置和代码编写,无法一套架构满足多样化业务逻辑的同时,支持系统扩展。

结合内部场景经验,我们基于规则引擎的设计思路构建了垂域的逻辑引擎 — LogicEngine

B端(问卷管理)和C端(问卷投放)对于问卷逻辑的关注点不同,B端主要是问卷逻辑的可视化编排,C端主要是规则的应用,基于此LogicEngine主要实现逻辑编排和逻辑解析的管理。

过程中可以看到,LogicEngine不仅满足了复杂的业务场景拓展,还能够将视图部分进行解耦,下面会进行具体讲解。

原理

1、领域设计

问卷逻辑编排实际上是问卷题目/选项之间关系的表达,即一系列规则条件和多目标的关系构建和应用。领域模型上分为RuleNode和ConditionNode:

  • ConditionNode:维护规则条件。
  • RuleNode :维护规则条件和目标。

2、架构设计

2.1、RuleBuild

逻辑配置管理

RuleBuild主要用于将问卷逻辑进行规则化描述,即构建RuleNode和ConditionNode:

逻辑关系管理

除了考虑逻辑配置的增删查改,RuleBuild还需要维护题目之间的逻辑关系。

比如配置题目2选中了选项1则显示题目3,那么题目2或题目3的删除会使规则组中存在一条失效的规则配置。

RuleBuild提供了用于题目关系管理的方法:

  • findTargetsByFields: 根据前置题查找目标题
  • findTargetByScope:根据目标作用范围查询目标题
  • findConditionByTarget:根据目标题查找关联条件中的前置题

2.3、RuleMatch

RuleMatch的核心是动态计算结果,即根据用户的答题内容,进行题目显隐渲染。

实现上其实就是对规则组进行遍历匹配,考虑到规则配置存在组合嵌套的情况,我们采用了hash表的方式,将RuleNode和ConditionNode构造成一个题规则Map:

[target][scope]: [{
  [field][operator][condition.value]: match(formvalues) // formvalues:整卷的输入内容
}]

举例,用户选中了题目1 (id: "data472")的选项2 (id: "115020"),需要显示题目5 (data515):

...
  "data472question": [{
      "data515in115020": true,
      ...
  }],
  ...

关于结构字段可查看:《问卷业务协议规范》《问卷业务协议规范和原理》

场景案例

显示逻辑配置(B端)

题目schema:

// 为演示更清晰,转换成了文字描述
题目1: "data515"
    选项1: "115019"
    选项2: "115020"
题目2: "data234"
    选项1: "392497"
    选项2: "097235"
题目4: "data700"
    选项1: "328312"
    选项2: "079366"
题目5: "data472"

显示逻辑配置:

通过RuleBuild得到如下规则配置:

显示逻辑渲染(C端)

题规则Map:

借助computed有计算缓存,实现渲染:

// QuestionWrapper.vue
<QuestionRuleContainer ...></QuestionRuleContainer>
...
// 显示逻辑:题目是否需要显示。当match有变化的时候触发重新计算
const logicShow = computed(() => {
  const result = showLogicEngine.value.match(fieldId, 'question', formValues)
  return result // true || false
})

扩展跳转逻辑

下面分析如何基于以上架构快速实现跳转逻辑。

异同点分析

在 显示逻辑 中表述为:

题目1 符合条件 且 题目2 符合条件,则显示题目4
题目1 符合条件,则显示题目5

在 跳转逻辑 中表述为:

题目1 符合条件 或 题目2 符合条件,则跳转到题目4(隐藏题目3)
题目1 符合条件,则跳转到题目5(隐藏题目34)

在跳转逻辑里,条件需要控制多个题目,最终作用到的是条件题到目标题之间的那批题目。

如上:题目1符合条件时,需要根据题目5查找题目1到5之间的题目进行隐藏,即题目3和4。

扩展实现

RuleBuild无需调整,视图上区别于显示逻辑的表单配置,我们采用了流程图的形式:

扩展RulesMatch满足跳转逻辑:

// RulesMatch.ts
match() {
  ...
  Array.from(this.conditions.entries()).some(([, value]) => {
    return !!value.match(fact)
  })
  ...
}

检索要跳过的题目:

// 获取条件题关联的多个目标题匹配情况
getResultsByField(field: string, fact: Fact) {
  ...
  return rules.map(([, rule]) => {
    return {
      target: rule.target,
      result: this.match(rule.target, 'question', fact, 'or')
    }
  })
}

总结

项目基于LogicEngine已经实现了基础的显示及跳转逻辑,支持的功能如下,欢迎对这个方案比较感兴趣的同学一起交流:

写在最后

一波福利,参与年底活动,获官方精美周边。

参与方式:转载文章、原创文章、项目案例分享

活动截止时间:2025年02月28

更多详情:github.com/didi/xiaoju…

资料推荐:

「开源」规则引擎/流程引擎,致力于解决灵活繁复的硬编码问题

从0到1:构建强大且易用的规则引擎

搞定营销活动-谈一谈规则引擎在活动系统中的落地

手把手搭建业务规则引擎 (Rule Engine)

前端领域驱动设计的一些思考

网易考拉规则引擎平台架构设计与实践 网易数帆

B 站新一代 golang 规则引擎的设计与实现

风控逻辑利器---规则引擎

《解构领域驱动设计》张逸

《大道至简:软件工程实践者的思想》周爱民

《架构整洁之道》罗布特C.马丁