Java集成飞书审批流0基础指南
概述
飞书 是一款企业IM软件,但同时它也逐步的变成了企业OA平台。大家已经习惯了将各种事件通知、审批流程统一收拢到飞书。
飞书审批的概念和流程比较复杂,这个复杂度不是飞书带来的,而是审批流本身带来的。
本文希望帮助大家建立飞书审批流的认知。
审批流的概念
审批流是一个工作流,所谓的工作流一般就是一个DAG。用一个有向无环图来编排业务流程,业务事件驱动流程自动化的流转。
flowchart
A["开始事件"] --> B["订单创建"]
B --> C{"支付校验?"}
C -- 成功 --> D["库存检查"]
C -- 失败 --> E["取消订单"]
D --> F{"库存充足?"}
F -- 是 --> G["生成发货单"]
F -- 否 --> H["触发补货"]
G --> I["物流发货"]
I --> J["通知客户"]
J --> K["结束事件"]
H --> L["结束事件(缺货终止)"]
E --> M["结束事件(支付失败)"]
审批流在工作流的基础上,多了两点:
- 流程中的每个节点,需要【审批人】来决策【通过】或【拒绝】。
- 每个流程需要携带一些用于展示的表单数据,用来给【审批人】提供决策上下文。
总结来说,工作流可能就是一个自动化流程,一旦流程开始,可以自动化的进行,不需要用户参与。但是审批流的目的就是让【审批人】参与,审批流中的流程节点,就是让【审批人】来提交他的决策。
在日常开发过程中,总有围绕业务建设审批流程,推动单据状态流转的场景。一般来说我们有自建审批流程或集成飞书审批流程两个方案。
自建审批流程
审批流程就是一个工作流,只不过这个工作流需要由用户的审批动作来驱动状态变化。
如果要自建的话,我们要做的事情就是:
- 【后端】选一个开源的工作流平台,结合具体设计、编排审批流程。这种的有很多,比较知名的比如:Activiti/Activiti
- 【前端】围绕设计的工作流,建设匹配的前端UI。主要包含 表单展示、状态展示、流程展示、操作按钮
- 【后端】集成IM平台,如飞书、钉钉,完成进度通知、结果通知等
飞书审批流程
飞书审批流程是一个平台化的SAAS服务,将业务无关的存储啊、通知啊、前端UI都做了统一的设计、封装。
我们只需要按业务来白屏化的拖拽设计好表单(低代码),然后白屏化的设计好决策流程(工作流),然后就可以【人工创建】审批或者使用【代码创建】审批了。
飞书审批的概念
在飞书审批的开放平台体系中,审批定义 (Approval Definition)、审批实例 (Approval Instance) 与 审批任务 (Approval Task) 是理解其流程三个核心概念。
简单来说,它们的关系可以类比为 “模板”、“申请单” 和分配给具体审批人的 “待办事项”。
审批定义(Definition) – 设计和定义模板
审批定义 (Approval Definition) 是对一类特定审批事项的抽象和规范,它是一个可重复使用的 模板。在企业发起任何审批之前,管理员都需要先创建好对应的审批定义。
一个审批定义主要由两部分组成:
- 表单 (Form):定义了审批申请需要填写的内容结构。它由一系列控件(如文本框、日期选择器、人员选择器等)组成,规定了用户需要提交哪些信息。例如,一个“请假审批定义”的表单可能包含“请假类型”、“开始时间”、“结束时间”和“请假事由”等字段。
- 流程 (Process):定义了审批申请需要经过的流转路径。它规定了从申请提交开始,需要经过哪些审批节点(由谁来审批、会签还是或签),直到最终审批结束(通过或拒绝)。
每个审批定义都有一个全局唯一的 approval_code,作为调用 API 时指定模板的唯一标识。
审批实例(Instance) – 基于模板实例化的流程
审批实例 (Approval Instance) 是用户基于某个审批定义(模板)发起的 一次具体的申请单。当员工填写完表单并点击“提交”后,系统就会生成一个审批实例。这个实例会承载着用户填写的全部数据,并作为一个整体,在预设的流程中流转。
每个实例都有一个全局唯一的 instance_code,用于追踪和操作这次具体的审批申请,也作为调用 API 时指定实例的唯一标识。
审批任务(Task) – 审批人的决策任务
如果说“审批实例”是一张正在财务部和行政部之间流转的报销单,那么 审批任务 (Approval Task) 就是这张报销单流转到财务部时,系统派发给财务“张经理”的那个具体的 “待办审批事项”。
当一个审批实例流转到流程中的某个节点时,系统会为该节点指定的审批人创建一个或多个审批任务。
- 一对一:如果节点审批人只有一个(例如“直属主管”),则会产生一个任务。
- 一对多:如果节点审批人有多个(例如需要“三位总监会签”),则会为每位总监都创建一个专属的审批任务。
每个任务都对应一个具体的审批人,等待其完成“同意”、“拒绝”或“转交”等操作。审批人处理完自己的任务后,审批实例才会根据流程规则判断是否要流转到下一个节点。
graph LR
%% 1. 先定义所有节点
A["审批定义 (模板)<br/>Approval Code: 请假流程"]
B{"审批实例 (申请单)<br/>Instance Code: inst_001"}
C("流程节点: 主管审批")
T1["审批任务<br/>审批人: 李主管"]
E["流程结束<br/>状态: 通过"]
%% 2. 再定义所有连接关系
A -- "创建" --> B
B -- "流转至" --> C
C -- "生成" --> T1
T1 -- "同意后" --> E
%% 3. 最后将节点分组
subgraph "设计阶段"
A
end
subgraph "申请与处理阶段"
B; C; T1; E;
end
飞书审批事件回调
审批实例和审批任务,都关联了一个有限状态机来控制生命周期。会根据流程进度来进行状态流转。
当审批流程中发生某个事件(例如,用户提交申请、审批人同意/拒绝、申请人撤销等)时,系统会发布一个对应的事件。订阅了该事件的微服务就会接收到通知,并根据事件的类型和当前状态,触发相应的状态流转。
业务上我们最关心的是【审批实例】的状态,审批实例代表了整个申请单的生命周期。其状态反映了审批的整体进展,也决定了这个流程是被通过还是被拒绝。
核心状态
- PENDING (待处理):
- 进入条件:用户成功提交审批申请后,审批实例的初始状态。
- 流转逻辑:
- 当所有流程节点都未完成,或者正在等待某个节点的审批人处理时,实例处于此状态。
- 事件驱动:当所有相关的审批任务都被处理(例如,所有会签任务完成),且结果为最终通过,实例状态会从
PENDING转换为APPROVED。如果其中任一任务结果导致拒绝,则转换为REJECTED。
- APPROVED (已通过):
- 进入条件:审批实例流经所有必要的审批节点,所有任务均按流程要求完成,且最终结果为同意。
- 流转逻辑:这是实例的最终状态之一,表示审批流程圆满完成。通常不会从
APPROVED再次流转到其他状态(除了极少数后台操作可能会将其归档或删除)。 - 事件驱动:所有下游审批任务都“通过”的事件汇总后,会驱动实例进入此状态。
- REJECTED (已拒绝):
- 进入条件:审批实例在任一流程节点被审批人拒绝。
- 流转逻辑:这是实例的最终状态之一,表示审批流程终止于拒绝。
- 事件驱动:任一关键审批任务被“拒绝”的事件,会立即驱动实例进入此状态。
- CANCELED (已撤销):
- 进入条件:在审批仍在进行中(
PENDING状态)时,由申请人主动发起撤销操作。 - 流转逻辑:表示申请被主动终止。
- 事件驱动:申请人发起“撤销”操作的事件,会驱动实例进入此状态。
- 进入条件:在审批仍在进行中(
- DELETED (已删除):
- 进入条件:通常由管理员或在特定条件下通过API触发的删除操作。
- 流转逻辑:实例被移除。
事件驱动
流程上的【审批人】进行审批决策后,后驱动【审批实例】状态变迁。飞书平台提供了【发布订阅】机制,用于业务微服务感知状态变迁,从而完成业务单据/流程的状态流转。整体流程如下图示:
sequenceDiagram
participant BusinessMicroservice as 业务微服务<br/>(例如:订单服务)
participant FeishuApprovalSystem as 飞书审批平台
participant ApproverClient as 审批人飞书客户端
Note over BusinessMicroservice,FeishuApprovalSystem: 系统初始化/配置阶段 (一次性设置)
BusinessMicroservice->>+FeishuApprovalSystem: 0. [配置] 注册审批状态变更事件订阅<br/>(提供回调URL)
FeishuApprovalSystem-->>-BusinessMicroservice: 确认订阅成功
Note over BusinessMicroservice: 运行时业务流程
Note over BusinessMicroservice: 内部业务逻辑触发审批<br/>(例如:订单金额 > 1000元)
BusinessMicroservice->>+FeishuApprovalSystem: 1. 调用API创建审批实例<br/>(create instance)
FeishuApprovalSystem-->>-BusinessMicroservice: 返回 instance_code
FeishuApprovalSystem->>ApproverClient: 2. 推送审批任务卡片消息
Note over FeishuApprovalSystem: (内部处理:实例和任务状态变为 PENDING)
ApproverClient->>+FeishuApprovalSystem: 3. 审批人操作 (例如:点击"同意")
Note over FeishuApprovalSystem: 内部处理:<br/>- 任务状态变为 APPROVED<br/>- 实例状态变为 APPROVED
FeishuApprovalSystem-->>-ApproverClient: 返回操作成功
FeishuApprovalSystem->>BusinessMicroservice: 4. [事件通知] 推送审批状态变更事件<br/>(至注册的回调URL)
BusinessMicroservice->>BusinessMicroservice: 5. 处理事件回调,更新内部业务状态<br/>(例如:更新订单状态为 "已批准")
- 事件订阅注册 (配置阶段):这是实现自动化的基础。业务微服务需要提前在飞书开发者后台进行配置,向飞书审批平台注册订阅其关注的审批事件。同时,微服务会提供一个回调URL,作为飞书推送事件通知的接收地址。这是一个一次性的配置步骤。
- 审批发起:当业务微服务内部的业务规则触发审批需求时(例如,订单金额超过预设阈值),微服务主动调用飞书的API来创建一个审批实例,并将业务数据(如订单ID、金额、申请理由等)写入审批表单。
- 飞书内部流转与用户交互:飞书审批平台接收到请求后,开始其内部的审批流程。它根据审批定义找到对应的审批人,并通过消息、待办事项等方式,将任务推送给审批人的飞书客户端。审批人在飞书客户端上直接进行“同意”、“拒绝”等操作。
- 事件发布与推送:当审批任务或实例的状态发生任何改变时,飞书审批平台会发布一个事件。对于已订阅该事件的业务微服务,飞书会向其预留的回调URL发送一个包含事件详细信息的HTTP POST请求。
- 事件消费与业务闭环:业务微服务的回调接口接收到事件后,进行解析。根据事件内容(例如,审批实例已通过),执行其内部的后续业务逻辑,如更新数据库中的订单状态,从而完成整个业务流程的闭环。
飞书审批回调方式
飞书开放平台的事件订阅是实现应用与飞书生态实时互动的核心。其回调机制设计了两种不同的模式,以适应不同的网络环境和开发需求:
- 基于 HTTP 的短连接模式 (Webhook)
- 基于 WebSocket 的长连接模式。
这两种模式的本质,都是为了解决同一个问题:当飞书上发生某个被订阅的事件时(例如,有人给机器人发消息、审批状态发生变更),飞书平台如何将这个事件通知到你的业务服务。
1. HTTP 短连接模式 (Webhook)
这是业界标准的 Webhook 机制。你的业务服务需要提供一个公网可以访问的 URL 地址(即 Request URL)。
sequenceDiagram
participant Feishu as 飞书开放平台
participant Gateway as 公司飞书网关 (公网)
participant YourService as 你的业务服务 (内网)
Note over Feishu, Gateway: 前提:Hook URL已在飞书后台注册
Feishu->>Feishu: 1. 内部事件发生 (如: 审批完成)
Feishu->>+Gateway: 2. [HTTP POST] 推送事件到网关
Gateway->>+YourService: 3. 转发HTTP请求至内网服务
YourService-->>-Gateway: 4. 返回HTTP 200
Gateway-->>-Feishu: 5. 返回HTTP 200,表示收到
YourService->>YourService: 6. (异步)处理业务逻辑
工作流程:
- 配置:在飞书开发者后台,你为应用配置一个公网 URL,并选择订阅你关心的事件。飞书会向这个 URL 发送一次性的验证请求,以确认你对该地址的所有权。
- 事件触发:当飞书平台发生对应事件时。
- 事件推送:飞书服务器会向你配置的公网 URL 发起一个 HTTP POST 请求,请求的 Body 中包含了事件的所有信息(通常是 JSON 格式)。
- 服务响应:你的业务服务接收到这个 POST 请求后,需要在3秒内返回一个 HTTP 200 状态码,表示“已成功接收”。如果超时或返回非200状态码,飞书会认为推送失败,并按一定策略(15秒、5分钟、1小时…)进行重试。
- 业务处理:在返回 200 响应后,你可以异步地处理事件内容,例如解析 JSON 数据,根据
event_id进行幂等性判断,然后更新自己的业务数据。
优点:
- 技术栈通用,任何能够处理 HTTP 请求的后端服务都可以集成。
- 无状态,便于水平扩展和部署。
缺点:
- 必须有公网IP/域名,这对于本地开发调试或部署在内网(如公司防火墙内)的服务来说非常不便。
2. WebSocket 长连接模式
这种模式是为解决公网 IP 问题而设计的,它颠倒了连接发起的方向。
sequenceDiagram
participant YourService as 你的业务服务 (内网)
participant Feishu as 飞书开放平台
YourService->>+Feishu: 1. [WebSocket] 主动发起长连接请求
Feishu-->>-YourService: 2. 连接建立成功
Feishu->>Feishu: 3. 内部事件发生 (如: 审批完成)
Feishu-->>YourService: 4. [WebSocket] 通过已建立的连接推送事件
YourService->>YourService: 5. 处理业务逻辑
工作流程:
- 连接建立:你的业务服务主动与飞书的事件推送网关建立一个 WebSocket 长连接。这个过程通常由飞书官方提供的 SDK 封装好了。
- 心跳维持:连接建立后,SDK 会自动处理心跳包,以保持连接的活性。
- 事件触发:当飞书平台发生对应事件时。
- 事件推送:飞书服务器会通过已经建立好的 WebSocket 通道,将事件信息直接推送给你的业务服务。
- 服务处理:你的服务在收到消息后,同样需要在3秒内处理完毕(在SDK层面体现为处理函数不抛出异常)。SDK 内部已经处理了验签和解密,你可以直接获取到明文的事件数据。
优点:
- 无需公网IP,服务可以部署在任何地方,包括你的个人电脑或公司内网,非常适合开发和调试。
- 安全便捷,SDK 自动完成了安全校验和心跳维持,开发者无需关心底层细节。
缺点:
- SDK 强绑定,灵活性相对较低。
- 需要维护长连接的状态,对服务的部署和资源有一定要求。
飞书审批流设计
详细内容可以参考:
创建流程
创建 审批流 也需要 审批哦。
开发者模式
在浏览器地址栏添加devMode=on后激活开发者模式,才可以看到审批Code和后续的自定义ID字段。
表单设计
拖拽即可
表单字段 自定义ID 需要和后续的代码中使用的 字段名 保持一致
流程设计
审批节点 自定义ID 需要和后续的代码中使用的 nodeId 保持一致
注意事项
1. 事件推送
飞书事件回调机制虽然好用,但想让你的系统稳定运行,有两点特别要注意:
- 业务幂等性:同一条消息,处理N遍也得没毛病!
- 啥意思? 飞书发消息,经常是“至少一次”送达,这意味着同一个事件你可能会收到好几次。你得保证,就算收到重复消息,你的业务逻辑处理起来也得和只收到一次的效果一样,不能重复操作、不能把数据弄乱。
- 咋处理? 业务上做好幂等处理!
- 推拉结合:光靠飞书推,消息还是会丢!
- 为啥? 你服务挂了、重启了,或者飞书那边重试次数用完了,有些事件就可能永远到不了你这儿。
- 咋处理? 搞个“推拉结合”,超过一定时间没有收到事件时,
fetchInstance查询下审批实例就会得到状态推送
理解并做好这两点,你的系统就能更稳健地和飞书打配合了!
2. 长连接
- 长连接模式下接收到消息后,需要在 3 秒内处理完成且不抛出异常,否则会触发超时重推机制。
- 每个应用最多建立 50 个连接(在配置长连接时,每初始化一个 client 就是一个连接)。
如果集群规模特别大,比如订单服务有200个节点,那么不应该由订单服务订阅事件。应该交给一个后台服务来订阅事件。后台服务一般不会有那么大规模
- 长连接模式的消息推送为 集群模式,不支持广播,即如果同一应用部署了多个客户端(client),那么只有 其中随机一个 客户端会收到消息。
总结
本文深入探讨了飞书审批从核心概念到集成实践的全过程,旨在解决业务开发中普遍存在的审批流集成复杂性问题。





