Skip to content

Commit 11d1cec

Browse files
committed
docs(zh-Hans): translate the new Build deep-dive pages (11 pages)
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_0145MY7RdJr8KuRMyn5oKAR6
1 parent 6d1e1e9 commit 11d1cec

13 files changed

Lines changed: 1573 additions & 4 deletions
Lines changed: 143 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,143 @@
1+
---
2+
title: 审批流程
3+
description: 把记录路由给人签核 —— 同时不让自动化悄悄绕过行级安全。
4+
---
5+
6+
# 审批流程
7+
8+
**审批就是带审批节点的[流程](/docs/build/automation/flows):流程暂停,直到有人批准或拒绝,然后沿匹配的分支继续。**没有需要另学的独立审批引擎 —— 触发器、分支、错误处理都与任何其他流程完全一致。本页新增的内容是审批节点本身,以及两个与路由同样重要的访问决策。
9+
10+
## 谁做什么
11+
12+
三个角色,刻意分离:
13+
14+
| 角色 | 能做 | 需要 |
15+
|---|---|---|
16+
| **构建者** | 编写和编辑审批流程 | `manage_metadata`(通常是 Studio 用户) |
17+
| **提交人** | 提交进入流程的记录 | 正常的记录访问 —— 不需要任何自动化配置权限 |
18+
| **审批人** | 处理审批请求(批准 / 拒绝) | 由其权限集授予的能力 |
19+
20+
最终用户提交记录、处理请求;他们从不编辑自动化。把自动化配置界面挡在消费者应用之外。
21+
22+
## 以谁的身份运行 —— 安全决策
23+
24+
流程通过 `runAs` 声明运行身份,对审批而言,正是这个决策防止流程悄悄绕过行级安全:
25+
26+
| `runAs` | 数据操作以谁运行 | 何时使用 |
27+
|---|---|---|
28+
| `'user'`(默认) | **提交人**,遵守其 RLS | 流程只触碰提交人本来就能看到的记录 |
29+
| `'system'` | **提权** —— 绕过 RLS | 流程必须读写提交人看不到的记录(写入总账、通知一个拥有提交人不可见行的审批人) |
30+
31+
**显式**声明提权,让它可见而非意外。默认的 `'user'` 意味着审批流程无法悄悄给提交人跨租户或跨所有者的触达 —— 提权是自愿开启且可审计的。
32+
33+
> **提示:**由定时触发的升级流程没有触发用户 —— 它必须 `runAs: 'system'` 才能行动。这是提权的正当场景;"到处 `system` 好让它能跑"才是反模式。
34+
35+
## 审批节点
36+
37+
节点声明**谁来审批**以及多个决策如何聚合:
38+
39+
```ts
40+
{
41+
id: 'manager_approval',
42+
type: 'approval',
43+
label: 'Manager Approval',
44+
config: {
45+
approvers: [{ type: 'field', value: 'owner_manager_id' }],
46+
behavior: 'unanimous',
47+
approvalStatusField: 'approval_status',
48+
lockRecord: true,
49+
},
50+
}
51+
```
52+
53+
| 配置 | 作用 |
54+
|---|---|
55+
| `approvers` | 谁必须决策 —— 具名用户、岗位,或从记录字段解析出的用户(如提交人的经理) |
56+
| `behavior` | 多个审批人如何聚合,如 `'unanimous'`(一致同意) |
57+
| `approvalStatusField` | 可选的记录字段,插件把请求状态镜像进去 |
58+
| `lockRecord` | 请求待定期间锁定记录、禁止编辑 |
59+
60+
> **警告 —— `position` vs `role`**`{ type: 'position', value: 'finance_manager' }` 路由给某个岗位的持有者(`sys_user_position`)。而 `role` 审批人类型是组织成员层级(`sys_member.role``owner`/`admin`/`member`)—— 把岗位名写成 `type: 'role'` 谁也匹配不上,请求就会卡住。`os lint` 会标记这个问题(`approval-role-not-membership-tier`)。
61+
62+
## 一个完整的审批流程
63+
64+
把大额提案路由给负责人的经理,再按决策分支:
65+
66+
```ts
67+
export const opportunityApproval = defineFlow({
68+
name: 'opportunity_approval',
69+
label: 'Opportunity Approval',
70+
type: 'record_change',
71+
status: 'active',
72+
runAs: 'user', // 用提交人的 RLS,除非某一步确实需要更多权限
73+
nodes: [
74+
{
75+
id: 'start',
76+
type: 'start',
77+
config: {
78+
triggerType: 'record-after-update',
79+
objectName: 'opportunity',
80+
condition: "record.amount >= 50000 && record.stage == 'proposal'",
81+
},
82+
},
83+
{
84+
id: 'manager_approval',
85+
type: 'approval',
86+
label: 'Manager Approval',
87+
config: {
88+
approvers: [{ type: 'field', value: 'owner_manager_id' }],
89+
behavior: 'unanimous',
90+
approvalStatusField: 'approval_status',
91+
lockRecord: true,
92+
},
93+
},
94+
{ id: 'mark_approved', type: 'update_record', label: 'Mark Approved' },
95+
{ id: 'mark_rejected', type: 'update_record', label: 'Mark Rejected' },
96+
{ id: 'end', type: 'end' },
97+
],
98+
edges: [
99+
{ id: 'e1', source: 'start', target: 'manager_approval' },
100+
{ id: 'approved', source: 'manager_approval', target: 'mark_approved', label: 'approve' },
101+
{ id: 'rejected', source: 'manager_approval', target: 'mark_rejected', label: 'reject' },
102+
{ id: 'e4', source: 'mark_approved', target: 'end' },
103+
{ id: 'e5', source: 'mark_rejected', target: 'end' },
104+
],
105+
});
106+
```
107+
108+
注意带标签的边:`approve``reject` 命名了决策之后的分支。永远要建模拒绝路径 —— 只有批准分支的流程会让被拒绝的记录搁浅。
109+
110+
**多级审批:**串联多个 `approval` 节点。**并行审批:**[流程](/docs/build/automation/flows)中的聚合节点模式。
111+
112+
## 审批人体验到什么
113+
114+
`@objectstack/plugin-approvals` 包持有持久化的审批状态。请求待定期间:
115+
116+
1. 插件持久化请求(`sys_approval_request`)以及针对它的每个决策(`sys_approval_action`)—— 你的审批历史是可查询的数据。
117+
2. 设置 `lockRecord: true` 时,记录被锁定、禁止编辑,直到决策落地。
118+
3. 如果设置了 `approvalStatusField`,记录自身的字段会镜像请求状态,视图和报表就能按它筛选。
119+
4. 审批人批准或拒绝;插件沿匹配的边恢复被暂停的流程。
120+
121+
批准本身也是被门控的操作。把"可以批准"建模为一个能力(如 `approve_invoice`),由审批人的权限集授予,并把批准操作的 `requiredPermissions` 建立在它之上 —— 这样门控就在 **UI 和服务端**两侧同时强制,而不只是从屏幕上藏起来。
122+
123+
## 最佳实践
124+
125+
| 应该 | 不应该 |
126+
|---|---|
127+
| 定义清晰的进入条件 | 设置过多的审批环节 |
128+
| 设置合理的超时时间 | 把审批做得过于复杂 |
129+
| 在合适的场景允许撤回 | 忘记拒绝路径 |
130+
| 通知所有相关方 | 把审批人写死 |
131+
| 追踪审批历史 | 只在 UI 层门控"批准" |
132+
| 默认 `runAs: 'user'`,一步一步地提权 | 到处设置 `runAs: 'system'` "好让它能跑" |
133+
134+
## 下一步
135+
136+
| 页面 | 原因 |
137+
|---|---|
138+
| [流程](/docs/build/automation/flows) | 本页所基于的流程参考 —— 触发器、步骤、错误处理 |
139+
| [工作流](/docs/build/automation/workflows) | 约束审批所处的生命周期 |
140+
| [操作](/docs/build/interface/actions) | 把提交和批准做成界面上的按钮 |
141+
| [CEL 表达式](/docs/reference/cel) | 进入条件背后的语言 |
142+
| [Email](/docs/configure/email) | 通知审批人与相关方 |
143+
| [自动化总览](/docs/build/automation) | 选择器:流程 vs 工作流 vs 审批 |
Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,39 @@
1+
---
2+
title: 自动化
3+
description: 为任务选对工具 —— 流程管步骤,工作流管状态,审批管人工签核。
4+
---
5+
6+
# 自动化
7+
8+
**自动化以声明的方式把业务逻辑挂到数据模型上 —— 作为由运行时执行的元数据 —— 而不是散落在应用代码里。**三个工具覆盖全部场景,一开始就选对,能省去日后返工。
9+
10+
## 我该用哪一个?
11+
12+
| 你需要 || 阅读 |
13+
|---|---|---|
14+
| "X 发生时,做 Y" —— 由记录变更、定时或按钮点击驱动的步骤 | **流程** | [流程](/docs/build/automation/flows) |
15+
| "这条记录只能按这些事件在这些状态间移动" —— 受控的生命周期 | **工作流**(状态机) | [工作流](/docs/build/automation/workflows) |
16+
| "必须有人签核后才能继续" —— 路由给人的决策 | **审批** | [审批流程](/docs/build/automation/approvals) |
17+
18+
经验法则:**状态**用工作流建模,**步骤**用流程建模。审批不是独立引擎 —— 审批就是在审批节点暂停、等人决策的流程。
19+
20+
## 它们如何组合
21+
22+
三者是互补而非竞争:
23+
24+
- **工作流**约束哪些生命周期迁移是合法的 —— 状态、迁移、守卫条件,仅此而已。
25+
- **流程**执行这些迁移周边的副作用:发邮件、更新记录、调用外部服务、等待、分支。
26+
- 流程内的**审批节点**阻塞执行直到审批人行动,然后沿批准或拒绝分支继续。
27+
28+
所以"工单从 new → assigned → resolved"是工作流;"升级时通知经理"是流程;"超过 5 万美元的折扣需要财务经理签核"是带审批节点的流程。
29+
30+
> **提示:**如果需求读起来是"X 发生时,做 Y",那就是流程。如果读起来是"这条记录绝不能跳过某个状态",那就是工作流。从这里出发,你几乎不会选错。
31+
32+
## 下一步
33+
34+
| 页面 | 原因 |
35+
|---|---|
36+
| [流程](/docs/build/automation/flows) | 完整的流程参考 —— 触发器、步骤类型、错误处理、CEL |
37+
| [工作流](/docs/build/automation/workflows) | 严格生命周期的状态、迁移与守卫条件 |
38+
| [审批流程](/docs/build/automation/approvals) | 审批节点、运行身份与审批人体验 |
39+
| [CEL 表达式](/docs/reference/cel) | 条件与守卫条件所用的表达式语言 |
Lines changed: 139 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,139 @@
1+
---
2+
title: 工作流
3+
description: 把记录的生命周期建模成状态机 —— 合法状态、带守卫条件的迁移,其余的交给流程。
4+
---
5+
6+
# 工作流
7+
8+
**工作流把记录的生命周期建模为有限状态机:记录可以处于的状态、在状态间移动它的事件,以及移动成立所必须满足的守卫条件。**当核心需求是"这个对象只能按这些事件在这些状态间移动"时,就用它。
9+
10+
不存在独立的 Salesforce 式 Workflow Rule 编写类型。旧的"工作流"概念被干净地一分为二:
11+
12+
- **状态机元数据**负责严格的生命周期迁移 —— 即本页。
13+
- **[流程](/docs/build/automation/flows)**负责事件触发或定时的自动化,包括[审批](/docs/build/automation/approvals)暂停。
14+
15+
## 工作流 vs 流程
16+
17+
| | 工作流(状态机) | 流程 |
18+
|---|---|---|
19+
| 建模 | *状态* —— 记录处于生命周期的哪里 | *步骤* —— 某事发生时做什么 |
20+
| 回答 | "此刻允许这个迁移吗?" | "我们要对它做什么?" |
21+
| 形态 | 状态、迁移、守卫条件 | 节点与边:触发器、动作、分支 |
22+
| 副作用 | 无 —— 它只做约束 | 全都有 —— 邮件、更新、HTTP、等待 |
23+
24+
两者组合使用:状态机约束迁移;当你需要通知、更新记录或调用外部系统时,由流程执行迁移周边的副作用。
25+
26+
## 定义状态机
27+
28+
一个必须按 `new → assigned → resolved` 移动、且带升级路径的支持工单:
29+
30+
```ts
31+
import type { StateMachineConfig } from '@objectstack/spec/automation';
32+
33+
export const caseLifecycle: StateMachineConfig = {
34+
id: 'case_lifecycle',
35+
initial: 'new',
36+
states: {
37+
new: {
38+
on: {
39+
ASSIGN: { target: 'assigned' },
40+
},
41+
},
42+
assigned: {
43+
on: {
44+
RESOLVE: { target: 'resolved', cond: 'has_resolution' },
45+
ESCALATE: { target: 'escalated' },
46+
},
47+
},
48+
escalated: {
49+
on: {
50+
RESOLVE: { target: 'resolved', cond: 'has_resolution' },
51+
},
52+
},
53+
resolved: {
54+
type: 'final',
55+
},
56+
},
57+
};
58+
```
59+
60+
把它当契约来读:`new` 状态的工单只能被分派。`assigned` 状态的工单可以被解决 —— 但只有 `has_resolution` 守卫条件通过时 —— 或者被升级。`resolved` 是终态;再没有什么能移动它。
61+
62+
## 解剖
63+
64+
|| 声明什么 |
65+
|---|---|
66+
| `id` | 状态机的标识符 |
67+
| `initial` | 每条新记录的初始状态 |
68+
| `states` | 状态名 → 其出向迁移的映射 |
69+
| `on` | 该状态响应的事件(`ASSIGN``RESOLVE`……) |
70+
| `target` | 事件把记录移动到的状态 |
71+
| `cond` | 迁移触发前必须满足的守卫条件 |
72+
| `type: 'final'` | 终态 —— 没有出向迁移 |
73+
74+
### 守卫条件
75+
76+
守卫条件(`cond`)让迁移带上前提:上例中,只有当 `has_resolution` 成立时 `RESOLVE` 才能到达 `resolved`。守卫条件把"没有解决方案就不能关单"编码成结构性规则,而不是散落在 UI 代码里的验证。
77+
78+
### 事件,而非字段写入
79+
80+
迁移由具名**事件**`ASSIGN``ESCALATE`)触发,而不是对状态字段的任意编辑。这正是重点:状态机定义了合法移动的完整集合,未声明的一律不可能发生。
81+
82+
> **提示:**保持状态机最小 —— 状态、迁移、守卫条件。一旦你想"然后再发封邮件",你就已经离开了工作流的领地:把副作用放进一个响应该迁移的[流程](/docs/build/automation/flows)
83+
84+
## 副作用属于流程
85+
86+
状态机只描述合法迁移与守卫条件 —— 别无其他。当某次迁移应该**点什么(通知销售、写入关闭日期、调用外部系统)时,给状态机配上一个由记录变更触发的流程:
87+
88+
```ts
89+
export const dealClosedWon = defineFlow({
90+
name: 'deal_closed_won',
91+
type: 'record_change',
92+
nodes: [
93+
{
94+
id: 'start',
95+
type: 'start',
96+
config: {
97+
triggerType: 'record-after-update',
98+
objectName: 'opportunity',
99+
condition: "record.stage == 'closed_won' && previous.stage != 'closed_won'",
100+
},
101+
},
102+
{ id: 'set_closed_date', type: 'update_record', label: 'Set Closed Date' },
103+
{ id: 'notify_sales', type: 'notify', label: 'Notify Sales' },
104+
{ id: 'end', type: 'end' },
105+
],
106+
edges: [
107+
{ id: 'e1', source: 'start', target: 'set_closed_date' },
108+
{ id: 'e2', source: 'set_closed_date', target: 'notify_sales' },
109+
{ id: 'e3', source: 'notify_sales', target: 'end' },
110+
],
111+
});
112+
```
113+
114+
状态机保证这笔交易是合法地*到达* `closed_won` 的;流程处理接下来发生的事。上面这样的条件是 CEL 表达式 —— 见 [CEL](/docs/reference/cel)
115+
116+
## 从 Workflow Rules 迁移
117+
118+
如果你来自有 Workflow Rules 的平台,对照表如下:
119+
120+
| 旧概念 | 当前对应 |
121+
|---|---|
122+
| Workflow Rule | 流程 |
123+
| 时间触发器 | 定时流程 |
124+
| 字段更新动作 | `update_record` 节点 |
125+
| 邮件提醒 | `notify` 节点 |
126+
| HTTP 调用 | `http` 节点 |
127+
| Approval Process | 带一个或多个 `approval` 节点的流程 |
128+
129+
判断标准:如果旧规则是"X 发生时,做 Y",就建成一个小流程。如果它是"这条记录必须在受控状态间移动",就把生命周期建成状态机,副作用交给流程。
130+
131+
## 下一步
132+
133+
| 页面 | 原因 |
134+
|---|---|
135+
| [流程](/docs/build/automation/flows) | 迁移周边的副作用 —— 触发器、步骤、错误处理 |
136+
| [审批流程](/docs/build/automation/approvals) | 作为流程内暂停的人工签核 |
137+
| [CEL 表达式](/docs/reference/cel) | 守卫条件与流程条件背后的语言 |
138+
| [数据建模](/docs/build/data) | 你正在约束其生命周期的对象 |
139+
| [自动化总览](/docs/build/automation) | 选择器:流程 vs 工作流 vs 审批 |

0 commit comments

Comments
 (0)