ObjectOS
参考

CEL 表达式

用于公式、谓词、调度和模板字符串的表达式语言 —— 通过五个标签模板暴露。

CEL 表达式

ObjectOS 在所有需要小型、安全、沙箱化表达式的地方使用 CEL(Common Expression Language):公式字段、校验规则、可见性谓词、共享条件、流程守卫、调度和模板字符串。

编写通过从 @objectstack/spec 导入的五个标签模板完成。它们都生成一个由运行时解析的小型 JSON 对象:

{ dialect: 'cel' | 'template' | 'cron', source: string }

Schema 源码: packages/spec/src/shared/expression.zod.ts

五个标签模板

模板Dialect用途示例
F`...`cel公式字段 —— 与记录一同存储的派生值F`record.amount * 0.1`
P`...`cel谓词 —— 用于校验/共享/可见性/条件的布尔值P`record.status == "open"`
cel`...`cel通用 CEL —— 当 F 或 P 都不合适时(例如参数值)cel`now() + duration("P30D")`
tmpl`...`template{{var}} 插值的字符串模板tmpl`Order from {{record.customer.name}}`
cron`...`cron调度 —— 标准 5 字段 cron 语法cron`0 9 * * 1-5`

FPcel 在求值时没有功能差异 —— 它们都运行 CEL。区分的存在是为了让 schema(和 AI Agent)知道表达式扮演什么角色,并让编辑器能进行类型检查(公式必须返回值,谓词必须返回 bool)。

导入

import { F, P, cel, tmpl, cron } from '@objectstack/spec'

各自的使用位置

Spec 上的字段标签示例位置
Field.expression(formula 类型)F*.object.ts 公式字段
Field.conditionalRequiredP对象字段
Validation.predicateP对象校验
SharingRule.conditionP共享规则
View.conditionalFormatting[].conditionP视图
Flow.step.when / Flow.transition.whenP流程
Action.guardPAction
通知主题/消息正文tmpl通知
Schedule.croncron调度流程/报表
任意值参数cel流程步骤输入

变量作用域

CEL 表达式在带有以下顶级变量的上下文中求值:

变量何时可用内容
record几乎总是当前正在求值的记录
previous更新 Hook/变更检测记录变更前的状态(或 null
inputAction、流程步骤用户提交的输入载荷
os.user总是{ id, roles: string[], permissions: string[] }
os.org总是组织/租户上下文
os.env总是暴露给表达式的环境变量

旧的 OLD / NEW 变量已在 M9.5 中移除。请使用 previousrecord

标准库

注册位置: packages/formula/src/stdlib.ts。 最常用的内置函数:

时间

函数返回说明
now()Timestamp钉在求值上下文 —— 单次查询内稳定
today()TimestampUTC 当日开始
daysFromNow(int)Timestamp未来日期
daysAgo(int)Timestamp过去日期

CEL 还包含原生 timestamp(...)duration(...)date.getDayOfWeek() 等 —— 参见 CEL spec

工具

函数用途
isBlank(x)nullundefined"" 或空列表时为 true
coalesce(a, b)第一个非空值
trim(s)去除空白
joinNonEmpty(list, sep)拼接非空条目

原生 CEL 字符串辅助函数(.contains(...).startsWith(...).matches(...).size())始终可用。

示例

公式字段 —— 行项目合计:

{ name: 'subtotal', type: 'formula', expression: F`record.quantity * record.unit_price` }

校验 —— 关闭日期必须在今日之后:

{ message: 'Close date must be in the future', predicate: P`record.close_date > today()` }

可见性 —— 仅向经理显示字段:

{ visibleIf: P`'manager' in os.user.roles` }

流程守卫 —— 金额小时跳过步骤:

{ when: P`record.amount >= 1000` }

调度 —— 工作日 9 点:

{ schedule: cron`0 9 * * 1-5` }

模板 —— 通知主题:

{ subject: tmpl`[{{record.priority}}] {{record.subject}}` }

错误

表达式在加载时编译。失败以带源码位置的 VALIDATION_ERROR 呈现:

{ "code": "VALIDATION_ERROR", "message": "CEL: unknown field 'amout' on Record", "details": { "field": "subtotal", "expression": "record.amout * 0.1" } }

无效表达式不会静默失败。畸形或引用未知字段的表达式会让 os compile 失败,并给出上面那条带定位的消息(对拼错的字段还包含 did-you-mean 提示)。运行时遇到坏表达式会抛出一个带归属信息的错误,而不是静默求值为 nullfalse,因此该失败会在日志和审计轨迹中可见,而不会悄悄污染一个公式值或一个守卫判定。

参见

On this page