跳到主要内容

条件语言的语义与评估模型:从概念到工程实践

本文系统介绍一种可配置、可扩展的条件语言(Condition Language),用于在复杂业务中进行无副作用的规则判定(如工作流流转、权限约束、功能开关等)。我们将覆盖基础概念、语法结构、语义分析与求值原则、典型应用、最佳实践、性能优化、边界处理,并给出可直接用于生产的通用代码范式。

核心理念:将“条件判定”与“副作用执行”严格分离,确保规则判断可测试、可组合、可扩展。


目录

  1. 基本概念与术语
  2. 语法与示例
  3. 语义分析:从条件树到 Evaluator
  4. 评估原则与短路策略
  5. 典型应用场景
  6. 最佳实践
  7. 性能优化策略
  8. 边界与异常处理
  9. 扩展设计与示例
  10. 总结

1. 基本概念与术语

  • 条件(Condition):由一个操作符(Op)和参数(Params)组成的表达式,可嵌套形成树状结构。
  • 操作符(Operator):定义布尔语义与求值规则。基础操作符包括:
    • Must(AND)、
    • Should(OR)、
    • MustNot(NOT)、
    • True / False(常量)、
    • InUserDomains(用户域成员匹配)。
  • 评估器(Evaluator):将条件树转换为可执行对象,提供 Eval() 方法输出布尔结果。
  • 上下文(Context):规则判定所依赖的业务上下文(例如某项目、某资源或某类型)。
  • 环境(Environment):运行时环境参数(例如变量绑定、运行模式开关)。

2. 语法与示例

条件采用树状 JSON 表达,示例:

{
"op": "must",
"params": {},
"children": [
{
"op": "should",
"params": {},
"children": [
{ "op": "true", "params": {} },
{
"op": "in_user_domains",
"params": {
"user_domains": [
{ "user_domain_type": "single_user", "user_domain_param": "user-1" },
{ "user_domain_type": "group", "user_domain_param": "group-42" }
],
"match": "any",
"allow_super_admin": true
}
}
]
},
{
"op": "must_not",
"params": {},
"children": [
{ "op": "false", "params": {} }
]
}
]
}

说明:

  • children 为子条件数组,支持递归嵌套。
  • InUserDomains 支持参数:
    • user_domains:数组,每项包含 user_domain_type 与 user_domain_param。
    • match:匹配模式,"any"(并集,默认)或 "all"(交集)。
    • allow_super_admin:是否允许“超管”绕过,布尔值,默认 true。

3. 语义分析:从条件树到 Evaluator

评估器工厂(EvaluatorHub)负责将条件树构造成 Evaluator。我们建议将“构造”与“校验”解耦,并支持严格模式。

示例(通用 Go 风格伪代码):

// Evaluator 定义
type Evaluator interface {
Eval() chan EvalResult
}

type EvalResult struct {
Bool bool
Error error
}

// 构造器与校验器
type EvaluatorBuilder func(params map[string]interface{}) (Evaluator, error)
type EvaluatorParamValidator func(params map[string]interface{}) error

// 工厂:支持注册、参数校验、严格构造
type EvaluatorHub struct {
builders map[string]EvaluatorBuilder
validators map[string]EvaluatorParamValidator
}

func NewEvaluatorHub() *EvaluatorHub {
return &EvaluatorHub{
builders: make(map[string]EvaluatorBuilder),
validators: make(map[string]EvaluatorParamValidator),
}
}

func (h *EvaluatorHub) Register(op string, builder EvaluatorBuilder) {
h.builders[op] = builder
}

func (h *EvaluatorHub) RegisterWithValidator(op string, builder EvaluatorBuilder, validator EvaluatorParamValidator) {
h.builders[op] = builder
if validator != nil {
h.validators[op] = validator
}
}

// 常规构造:递归构造 children,并注入 EvaluatorsKey
func (h *EvaluatorHub) NewEvaluator(cond *Condition, additional map[string]interface{}) (Evaluator, error) {
build := h.builders[cond.Op]
if build == nil {
return nil, fmt.Errorf("invalid evaluator op %q", cond.Op)
}
params := mergeParams(cond.Params, additional)
children := cond.Children()
if len(children) == 0 {
return build(params)
}
evalChildren := make([]Evaluator, len(children))
for i, c := range children {
e, err := h.NewEvaluator(c, additional)
if err != nil {
return nil, err
}
evalChildren[i] = e
}
params["_evaluators"] = evalChildren
return build(params)
}

// 严格构造:先执行参数校验,再递归
func (h *EvaluatorHub) NewEvaluatorStrict(cond *Condition, additional map[string]interface{}) (Evaluator, error) {
build := h.builders[cond.Op]
if build == nil {
return nil, fmt.Errorf("invalid evaluator op %q", cond.Op)
}
params := mergeParams(cond.Params, additional)
if v := h.validators[cond.Op]; v != nil {
if err := v(params); err != nil {
return nil, err
}
}
children := cond.Children()
if len(children) == 0 {
return build(params)
}
evalChildren := make([]Evaluator, len(children))
for i, c := range children {
e, err := h.NewEvaluatorStrict(c, additional)
if err != nil {
return nil, err
}
evalChildren[i] = e
}
params["_evaluators"] = evalChildren
return build(params)
}

构造策略(支持 Context/Environment 注入与严格模式):

// 构造选项
type BuildOptions struct {
TeamID string
UserID string
Context Context
Env Environment
Strict bool
Additional map[string]interface{}
}

func BuildEvaluatorWithOptions(h *EvaluatorHub, cond *Condition, opts BuildOptions) (Evaluator, error) {
params := map[string]interface{}{
"_team_id": opts.TeamID,
"_user_id": opts.UserID,
"_context": opts.Context,
"_env": opts.Env,
}
for k, v := range opts.Additional {
params[k] = v
}
if opts.Strict {
return h.NewEvaluatorStrict(cond, params)
}
return h.NewEvaluator(cond, params)
}

4. 评估原则与短路策略

  • Must(AND):按序短路,遇到 false 立即返回 false;所有子项为 true 返回 true。
  • Should(OR):按序短路,遇到 true 立即返回 true;所有子项为 false 返回 false。
  • MustNot(NOT):对子项取反;空子项视作 false 的取反。
  • True/False:常量返回。
  • InUserDomains(用户域成员匹配):
    • match = "any":用户属于任意一个域即 true;统一批量检查后短路。
    • match = "all":用户需同时属于所有域;逐个检查,遇到 false 立即返回 false。
    • allow_super_admin:可选绕过开关;true 时具备“超管”身份可直接返回 true。

通用伪代码实现(去除环境依赖):

type InUserDomainsEvaluator struct {
TeamID string
UserID string
Context Context
Env Environment
UserDomains []UserDomain
MatchMode string // "any" | "all"
AllowSuperAdmin bool
}

func ValidateInUserDomainsParams(p map[string]interface{}) error {
// 1) 校验上下文与必要参数存在性
// 2) 校验 user_domains 项结构与类型,并可调用 ValidateUserDomain(teamID, domain)
// 3) 校验 match 与 allow_super_admin 类型与取值
return nil
}

func NewInUserDomainsEvaluator(params map[string]interface{}) (Evaluator, error) {
// 解析 TeamID/UserID/Context/Env
// 解析 match(默认 "any")、allow_super_admin(默认 true)
// 解析 user_domains,进行去重与有效性校验
// 返回评估器
return &InUserDomainsEvaluator{ /* ... */ }, nil
}

func (e *InUserDomainsEvaluator) Eval() chan EvalResult {
ch := make(chan EvalResult, 1)
go func() {
if len(e.UserDomains) == 0 {
ch <- EvalResult{Bool: false}
return
}
if e.MatchMode == "any" {
ok, err := IsMemberAny(e.TeamID, e.UserID, e.Context, e.Env, e.UserDomains)
if err != nil { ch <- EvalResult{Error: err}; return }
if ok { ch <- EvalResult{Bool: true}; return }
} else {
for _, d := range e.UserDomains {
ok, err := IsMember(e.TeamID, e.UserID, e.Context, e.Env, d)
if err != nil { ch <- EvalResult{Error: err}; return }
if !ok {
if e.AllowSuperAdmin && IsSuperAdmin(e.TeamID, e.UserID) {
ch <- EvalResult{Bool: true}; return
}
ch <- EvalResult{Bool: false}; return
}
}
ch <- EvalResult{Bool: true}; return
}
if e.AllowSuperAdmin && IsSuperAdmin(e.TeamID, e.UserID) {
ch <- EvalResult{Bool: true}; return
}
ch <- EvalResult{Bool: false}
}()
return ch
}

5. 典型应用场景

  • 工作流状态流转前置校验:在“流转”按钮触发前,校验操作者是否满足 InUserDomains、是否符合 Must/MustNot 组合条件。
  • 权限与资源访问控制:以 Should/MustNot 组合实现白名单/黑名单逻辑。
  • 功能开关与 A/B 测试:结合 Context/Environment 实现基于人群与环境变量的开关控制。
  • 数据操作前置约束:如批量操作前校验操作者是否具备某域成员身份或某角色集合。

6. 最佳实践

  • 规则可组合:以 Must/Should/MustNot 组合复杂规则,避免单一操作符过载。
  • 上下文可注入:通过构造选项统一注入 Context/Environment,保持规则与业务隔离。
  • 严格与宽松模式:生产侧建议启用严格模式(Strict),在构造阶段就拒绝无效参数;灰度或兼容场景可使用宽松模式。
  • 参数前校验:统一使用 RegisterWithValidator 注册带参数校验的 Evaluator,减少运行期不确定性。
  • 实例化幂等:构造器对 user_domains 做去重,避免重复评估。
  • 可观测性:为每个 Evaluator 打点记录耗时、命中率与错误分布;出现异常快速定位。

7. 性能优化策略

  • 短路求值:Must/Should 按序短路,避免不必要的子条件计算。
  • 批量化评估:InUserDomains 在 "any" 模式下批量查询并集,减少外部服务调用。
  • 预过滤:构造前过滤掉无效或已删除的用户域,降低评估时的分支。
  • 上下文预构建:将 TeamID/UserID/Context/Env 在构造时一次性注入,无需在评估时重复解析。
  • 缓存与版本化:结合“更新戳”(update stamp/version)思路做读侧缓存,保证读写一致性并避免主动失效。
  • 异步与并行:在长链路场景中,针对独立子条件采用并行评估并在父节点进行聚合(需注意顺序依赖与资源限制)。

8. 边界与异常处理

  • 空条件:空树或条件被全部过滤后视为“恒真”(根据业务策略可配置为恒真或恒假)。
  • 缺失参数:严格模式下立即拒绝;宽松模式下尝试默认值并记录告警。
  • 无效用户域:构造阶段校验并拒绝;或在过滤阶段剔除。
  • 树深限制:设定最大递归深度,防止异常配置导致栈溢出。
  • 外部服务不可用:InUserDomains 调用失败返回错误并上报;可配置失败降级策略。
  • 循环引用防护:在扩展操作符时谨慎设计,避免隐式依赖引起循环。
  • 超管绕过开关:必须明确由 allow_super_admin 控制,避免“隐式绕过”带来的安全风险。

9. 扩展设计与示例

  • 新操作符示例:字段比较
    • op: "field_equals",params: { "field": "priority", "value": "P1" }
    • 评估器读取 Context 中的字段并进行常量比较。
  • 时间窗口控制
    • op: "time_window",params: { "start": "...", "end": "..." }
    • 用于限制在特定时间范围内才允许操作。
  • InUserDomains 扩展示例(all 模式 + 关闭超管绕过):
{
"op": "in_user_domains",
"params": {
"user_domains": [
{ "user_domain_type": "group", "user_domain_param": "g-1" },
{ "user_domain_type": "role", "user_domain_param": "r-2" }
],
"match": "all",
"allow_super_admin": false
}
}
  • 工厂扩展示例:注册带校验的操作符
hub := NewEvaluatorHub()
hub.Register("must", NewMustEvaluator)
hub.Register("should", NewShouldEvaluator)
// 带参数校验注册
hub.RegisterWithValidator("in_user_domains", NewInUserDomainsEvaluator, ValidateInUserDomainsParams)

10. 总结

  • 条件语言强调“可配置、可组合、可扩展”,通过树状结构与通用操作符覆盖大多数业务需求。
  • 将判定与副作用执行解耦,保证规则的可测试与可回归。
  • 工程实践中,以“参数校验 + 严格构造 + 短路评估 + 观察性”构成闭环。
  • 推荐策略:默认开启严格模式(Strict),统一使用 RegisterWithValidator 注册评估器;InUserDomains 根据场景选择 match=any 或 match=all,并明确 allow_super_admin 的默认策略。

附:最小可用初始化示例(通用表达)

func init() {
hub := NewEvaluatorHub()
hub.Register("must", NewMustEvaluator)
hub.Register("should", NewShouldEvaluator)
hub.Register("must_not", NewMustNotEvaluator)
hub.Register("true", NewTrueEvaluator)
hub.Register("false", NewFalseEvaluator)
hub.RegisterWithValidator("in_user_domains", NewInUserDomainsEvaluator, ValidateInUserDomainsParams)
// 以 hub 为中心,在业务入口处统一构造 Evaluator
}
// 构造入口示例(带 Context/Environment 与严格模式)
e, err := BuildEvaluatorWithOptions(hub, cond, BuildOptions{
TeamID: "team-xyz",
UserID: "user-abc",
Context: ctx,
Env: env,
Strict: true,
Additional: map[string]interface{}{
// 自定义参数注入
},
})
if err != nil {
// 记录错误并拒绝本次操作
}
r := <-e.Eval()
if r.Error != nil {
// 上报错误
}
if r.Bool {
// 条件满足,继续执行后续副作用
}

这套通用模型可以被直接嵌入到你的工作流、权限、功能开关、数据操作等模块中。按需扩展操作符与校验器,即可适配不同业务域的复杂规则。