流程与自动化
声明式业务逻辑 —— 无论是描述给 AI 还是用 TypeScript 编写,运行时执行的都是同一份产物。
流程与自动化
Flow 是你不写服务端就能表达业务逻辑的方式。每个 Flow 都是声明式元数据,由运行时执行 —— 与对象、视图一样。这意味着 Flow 会同时出现在 os diff、审计日志、Console 的流程构建器以及 AI Builder 中。
多数客户通过对 AI 提问来创建 Flow:
"当一个高优先级工单在 'new' 状态停留 30 分钟时,在 Slack 上通知经理。"
AI 会生成下面的 Flow。本页描述其结构,以便你阅读和编辑。
在 stack 中启用该能力:
export default defineStack({
// ...
requires: ['automation'],
});三种流程类型
| 类型 | 触发方式 | 用于 |
|---|---|---|
| Autolaunched | 记录变更(insert/update/delete) | "用户注册时发欢迎邮件" |
| Scheduled | Cron 表达式或间隔 | "每晚 2 点标记过期任务" |
| Manual | 用户在 Console 点按钮,或 API 调用 | "审批发票"等 Action |
Autolaunched:对记录变更做出反应
// src/flows/welcome_email.ts
import { defineFlow } from '@objectstack/spec';
export const welcomeEmail = defineFlow({
name: 'welcome_email',
type: 'autolaunched',
trigger: {
object: 'sys_user',
when: 'after_insert',
},
steps: [
{
type: 'action',
action: 'send_email',
inputs: {
to: '{!trigger.record.email}',
subject: 'Welcome to {!org.name}',
body: 'Hi {!trigger.record.name}, welcome aboard.',
},
},
],
});变量插值:{!trigger.record.<field>}、{!org.<field>}、{!user.<field>}、{!step.<step-name>.output}。condition: 块中使用 CEL 表达式。
触发时机:
when | 何时触发 |
|---|---|
before_insert | 写事务内、INSERT 之前 |
after_insert | 提交之后 |
before_update | 写事务内、UPDATE 之前 |
after_update | 提交之后 |
before_delete | 写事务内、DELETE 之前 |
after_delete | 提交之后 |
before_* 流程可以修改正在写入的记录(计算字段、归一化数据)。after_* 流程异步运行,可以调用慢的外部服务。
Scheduled:按时钟运行
export const nightlyCleanup = defineFlow({
name: 'nightly_cleanup',
type: 'scheduled',
schedule: { cron: '0 2 * * *', timezone: 'America/New_York' },
steps: [
{
type: 'query',
query: { object: 'task', filter: 'status:open AND due_lt:now()' },
output: 'stale',
},
{
type: 'foreach',
items: '{!step.stale}',
do: [
{ type: 'update', record: '{!item.id}', fields: { status: 'overdue' } },
],
},
],
});由 @objectstack/service-job 能力承载 —— 见 Runtime Capabilities。
Manual:Action 与审批
export const approveInvoice = defineFlow({
name: 'approve_invoice',
type: 'manual',
inputs: {
invoice_id: { type: 'lookup', reference: 'invoice', required: true },
note: { type: 'textarea' },
},
steps: [
{
type: 'update',
record: '{!inputs.invoice_id}',
fields: { status: 'approved', approved_by: '{!user.id}' },
},
],
});在 Console 中作为 Invoice 视图上的按钮暴露,或通过 REST 调用:
curl -X POST https://app.example.com/api/v1/actions/invoice/approve_invoice \
-H 'Authorization: Bearer <token>' \
-d '{"inputs": {"invoice_id": "inv_123", "note": "OK"}}'步骤类型
| 步骤 | 用途 |
|---|---|
query | 通过 ObjectQL 读取记录 |
create / update / delete | 写对象 |
action | 调用内置或插件注册的 Action(email、webhook、AI 调用……) |
condition | 按 CEL 表达式分支 |
foreach | 遍历集合 |
parallel | 并发执行子步骤 |
wait | 暂停一段时长 / 至时间戳 / 至条件成立 |
subflow | 调用另一个 Flow |
approval | 阻塞直至用户审批(需要 @objectstack/plugin-approvals) |
条件与分支
{
type: 'condition',
when: 'trigger.record.amount > 10000',
then: [
{ type: 'action', action: 'send_slack', inputs: { /* ... */ } },
],
else: [
{ type: 'update', record: '{!trigger.record.id}', fields: { status: 'auto_approved' } },
],
}错误处理
每个步骤接受:
{
type: 'action',
action: 'send_email',
inputs: { /* ... */ },
retry: { attempts: 3, backoffMs: 1000, multiplier: 2 },
onError: 'continue' | 'fail' | 'rollback',
}对于 autolaunched 的 before_* 流程,onError: 'fail'(默认)会终止原始写事务。对 after_* 流程,原始写已经提交,失败的流程运行会落到任务重试队列。
公式与表达式(CEL)
条件、动态字段值和过滤表达式都接受 CEL(Common Expression Language) —— 谷歌为安全表达式求值设计的语言:
'amount > 10000 && account.tier == "enterprise"'
'duration(now() - created_at) > duration("30d")'
'has(record.notes) && record.notes != ""'CEL 是沙盒化的(无副作用、无 I/O)、服务端求值、并可在流程构建器中审计。
可视化构建器
Console 自带一个可视化流程构建器,它与声明式元数据来回往返 —— 非工程师可以编辑流程,序列化结果与你手写的 TypeScript 同形。
测试流程
os test --scenario "welcome email fires on signup"限制与最佳实践
- **保持 before-hook 简短。**它们会阻塞写事务。
- **用
wait代替长时运行的步骤。**休眠的流程会占用 worker;wait until会把 worker 还回池里。 - **用
parallel跑独立步骤。**默认是顺序执行。 - **幂等很重要。**重试可能让同一步骤跑两次;外部副作用应去重(用流程运行 id 作为键)。
- **审计敏感的 Action。**改权限或删记录的流程自身也应记录到
sys_audit_log。
下一步
- Webhooks —— 出站通知,常由流程触发
- Email ——
send_emailAction 的传输层 - AI Service —— 用于 LLM 步骤的
ai_callAction - API Access —— 从外部系统调用手动流程
- @objectstack/service-automation —— 执行引擎源码