在字节跳动,员工数量超10万,中后台每天处理的审批、报销、投放、权限等操作数以百万计。支撑如此高并发的关键,在于强大的流程引擎 + 状态机驱动 + 可视化配置系统。本篇将从架构拆解出发,并用 Vue + Node.js 实现一个简化版的审批流系统,助你构建可复用的业务引擎能力。
🧠 一、什么是审批流系统?
审批流系统是一种状态驱动 + 多角色协同 + 条件分支控制的业务流程系统:
- 提交 → 审批中 → 审批通过/驳回
- 支持节点签字、多人会签、加签、抄送、跳过等复杂操作
- 要求支持自定义流程模板、动态条件、角色权限隔离
🏗️ 二、字节跳动审批系统设计亮点
| 组件 | 设计策略 |
|---|---|
| 流程引擎 | 状态机驱动,定义节点状态、转移条件、角色绑定 |
| 表单系统 | 动态表单 + 数据校验 + 关联审批字段 |
| 流程可视化配置 | 支持运营拖拽配置流程,不需要研发干预 |
| 异常处理机制 | 节点异常处理机制(如重试、挂起、手动修复) |
| 数据权限控制 | 精细到字段级的数据访问与审批权限管控 |
| 审批节点策略 | 支持串行/并行、多人会签、加签等复杂配置 |
⚙️ 三、代码实战:简易审批流系统(Vue + Node.js)
我们以“内容投放申请”为例,实现一个提交审批、审批通过/驳回的基础流程。
1. 后端(Node.js)实现状态流转逻辑
模拟流程状态定义
const FLOW = {
SUBMITTED: '已提交',
PENDING: '待审批',
APPROVED: '已通过',
REJECTED: '已驳回',
};
审批处理逻辑
app.post('/api/approve', (req, res) => {
const { taskId, action } = req.body;
const task = taskStore[taskId];
if (!task) return res.status(404).send('任务不存在');
if (action === 'approve') task.status = FLOW.APPROVED;
else if (action === 'reject') task.status = FLOW.REJECTED;
res.send({ success: true, status: task.status });
});
2. 前端(Vue 3 + Naive UI)展示审批状态 + 操作按钮
任务卡片组件示意
<template>
<n-card title="任务审批">
<p>状态:{{ task.status }}</p>
<n-button @click="approve('approve')" type="success">通过</n-button>
<n-button @click="approve('reject')" type="error">驳回</n-button>
</n-card>
</template>
<script setup>
import { ref } from 'vue'
import axios from 'axios'
const task = ref({ id: 't1001', status: '待审批' })
async function approve(action) {
const res = await axios.post('/api/approve', {
taskId: task.value.id,
action,
})
task.value.status = res.data.status
}
</script>
🔍 四、工程扩展建议
| 需求 | 推荐实现方式 |
|---|---|
| 流程定义动态化 | 用 JSON DSL 或 BPMN 保存流程模板 |
| 节点条件逻辑 | 支持表达式解析(如 JS 规则引擎或自定义规则语法) |
| 异步任务处理 | 加入消息队列 + 状态轮询机制 |
| 节点可视化 | 使用 vue-flow、gojs、react-flow 等流程可视化组件 |
| 权限隔离 | 使用角色 + 组织架构映射实现审批权限配置 |
✍️ 五、总结与思考
- 审批系统 ≠ 简单的状态切换,而是一个“流程驱动的规则引擎”
- 字节跳动通过流程引擎 + 拖拽配置系统,将业务与开发解耦,降低研发成本
- 中后台场景复杂,建议将“审批流引擎”作为通用基础设施抽象出来复用
🎁 拓展推荐
- Camunda / Flowable BPMN 开源流程引擎
- 字节跳动《中后台流程引擎的演进与稳定性治理》
- Vue 可视化流程图组件推荐:vue-flow / jsplumb / gojs