外观
工作流
字数
8927 字
预计阅读
34 分钟
项目自带一个树形审批流程引擎,思路类似钉钉审批:流程是一棵从上往下的节点树。这对大多数审批场景已经够用,业务人员也更容易看懂。
流程也可以画成标准的 BPMN 流程图(见BPMN 设计器)。不过 BPMN 只是画法:发布时服务端把流程图转换成同样的节点树,运行的仍然是这个树形引擎。
为什么自研
- 现成的 BPMN 引擎要么需要 JVM(Flowable),要么生产环境需要商业许可,要么依赖 MongoDB;
- 自研引擎的每个动作就是待办表里的一次行变更,可以和业务数据在同一个事务里提交;
- 树形设计器保存的 JSON 就是引擎执行的 JSON,中间没有转换环节;
- BPMN 流程图只在发布时转换一次,变成和树形设计器一样的树。服务端只认它自己从流程图推出来的树,不采信浏览器传来的结果;
- 条件分支用结构化规则表示,不用表达式库,也就不存在任意代码执行的风险。BPMN 流程图里的脚本、表达式和监听器一律不接受。
零代码审批
不写代码也能新建一个审批:在 流程审批 → 流程管理 → 新建审批 中,按向导的四步完成。
- 基础设置:名称、分类、图标、说明,以及谁可以发起(用户、部门、角色,都不选表示所有人);
- 表单设计:用表单设计器拖出申请单;
- 流程设计:用树形设计器画审批流程。条件分支、"表单中的人员"这类审批人、字段权限,都可以直接使用上一步表单里的字段;
- 高级设置:流程管理员(修改它需要单独的权限,见审批数据里的字段权限),是否允许发起人撤销、审批人撤回。
向导和内置模板只做树形流程。想画 BPMN 流程图,在模型管理里新建模型,见BPMN 设计器。
随时可以"保存草稿"。点"发布"后,会依次保存表单、保存模型、发布新版本。其中一步失败时,已经完成的步骤会保留,点"重试"从失败的那一步继续。
从模板创建
向导里的"从模板创建"会复制一个内置模板,再按需要修改。项目内置了四个模板:
| 模板 | 表单 | 流程 |
|---|---|---|
| 通用审批 | 事由、详细说明、附件 | 发起人的部门负责人审批 |
| 请假 | 请假类型、开始日期、结束日期、请假天数(自动计算)、请假事由 | 超过 3 天由两级主管依次审批,其他情况由直属主管审批 |
| 报销 | 报销事由、费用明细(明细表,金额自动合计)、票据 | 合计超过 5000 由两级主管依次审批,其他情况由直属主管审批 |
| 加班 | 开始时间、结束时间、加班时长(小时)、加班事由 | 主管审批 |
模板本身是停用、未发布的,不会出现在"发起申请"里,只用来复制。
设计和发布流程
流程在 流程审批 → 流程管理 → 模型管理 中管理:
- 模型按分类分组显示,可以新建、编辑、启用停用、调整顺序、查看历史版本,还可以设置谁能发起、谁是流程管理员(需要单独的权限,见审批数据里的字段权限)、是否允许撤回和撤销;
- 新建模型时选"流程类型":树形或 BPMN,之后不能更改,见BPMN 设计器;
- 动态表单的模型(树形和 BPMN 都可以)可以选"绑定表单";其中树形模型还可以点"向导",用新建审批的向导修改;
- 点"设计"进入设计器:树形模型是树形设计器,BPMN 模型是 BPMN 设计器。修改可以随时保存为草稿,确认无误后发布为新版本。每个流程实例都记着自己发起时的版本,所以发布新版本不会影响已经在走的流程;
- 发布前,设计器用和服务端同一份代码检查整个流程(比如条件分支没有默认路径),错误列在图的上方,并定位到出错的节点。服务端发布时还会再检查一次;
- 每个版本都可以导出为 JSON 文件。树形模型还可以把 JSON 文件导入为下一个版本,方便在测试环境和正式环境之间搬运流程,导入时做和发布一样的检查。BPMN 模型的导入导出见下文;
- 设计页有没保存的修改时,离开页面前会先确认。
模型很多时
模型列表一次最多显示 200 个。超过时页面会提示,并且不能调整顺序。
BPMN 设计器
流程除了用树形设计器画,也可以画成标准的 BPMN 流程图。两种画法的节点设置相同,审批动作、审批中心、通知、超时处理、动态表单和业务表单、移动端审批也完全一样。
选择流程类型
在 模型管理 中新建模型时,选"流程类型":
| 流程类型 | 适合 |
|---|---|
| 树形 | 业务人员。在节点树上添加审批、抄送和分支,简单直观,也画不出发布不了的形状 |
| BPMN | 熟悉 BPMN 的人。画标准流程图,可以导入、导出 .bpmn 文件 |
流程类型创建后不能更改,模型列表里有"流程类型"一列。拿不准时选树形。BPMN 模型没有"向导"按钮,在设计页里编辑。
画布和属性面板
点"设计"打开 BPMN 设计器:左边是工具栏,中间是画布,右边停靠"属性"面板。
- 工具栏、元素旁边的快捷菜单和"更换类型"菜单,只提供能发布的元素(见下一节);
- 选中一个元素,在"属性"面板里设置它。审批节点和抄送节点的设置和树形设计器是同一套:审批人、多人审批方式、字段权限、驳回后、超时提醒等;
- 选中分支网关时,可以"添加分支",用"上移""下移"调整路径顺序;选中它的出线时,可以设置条件,或者"设为默认路径"。画布上带条件的连线显示为虚线。
必须保留 bpmn.io 标志
流程图用开源的 bpmn-js 绘制。它的许可要求画布右下角的 bpmn.io 标志可见、不被遮挡、不被改动。设计器和实例详情里的只读流程图都保留了这个标志,深色主题下会在它下面垫一块浅色底。基于本模板开发的项目也要遵守:不要用样式隐藏或修改它,也不要在画布右下角放任何常驻的东西。
能用哪些元素
| 元素 | 作用 |
|---|---|
| 开始事件 | 发起人,只能有一个 |
| 结束事件 | 流程结束,可以有多个 |
| 审批节点 | 和树形的审批节点一样 |
| 抄送节点 | 和树形的抄送节点一样 |
| 排他网关 | 条件分支:按路径顺序找第一条满足条件的路径,都不满足时走默认路径 |
| 包容网关 | 包容分支:满足条件的路径全部都走 |
| 并行网关 | 并行分支:所有路径同时进行 |
| 连线 | 分支网关的每条出线就是一条路径。条件只能设在排他网关、包容网关分出去的连线上 |
其他元素都不支持,比如子流程、脚本任务、服务任务、中间事件、泳池和泳道。
画的时候要遵守几条规则:
- 分叉和汇合成对:一个分支网关分出去的路径,最后要汇合到同一个同类型的汇合网关。多条线要合到一处,必须经过汇合网关,不能直接连到同一个节点上。用元素快捷菜单里的"在后面插入排他分支块"(并行、包容同理)可以一次画出成对的分叉和汇合;在"属性"面板里"添加分支",新路径会自动连到汇合网关;修改分支网关的类型时,汇合网关跟着改;
- 各条路径都直接走到结束、不再汇合的分支,只能放在流程的最后;
- 不能有回环:需要返工时用"退回"或"驳回";
- 名称最多 64 个字。
发布时的检查
发布前设计器先检查一遍,服务端发布时再检查一次,规则相同。不通过时,错误列在画布上方,出错的元素在画布上标红。下面这些会被拒绝:
- 回环、不成对的分叉和汇合、多个开始事件、走不到的元素、连线数量不对(比如两条线直接连进同一个审批节点);
- 泳池、泳道,以及上一节表格以外的元素;
- 脚本、表达式(比如
${...})、监听器,以及其他 BPMN 工具专有的扩展(比如camunda:、flowable:开头的属性)。条件只能用条件构造器设置; - 带 DOCTYPE 或实体声明的文件,以及超过 80 KiB(大约 90 个元素)的流程图。
保存草稿时只检查大小、DOCTYPE,以及文件是不是能正常读取的 BPMN XML,可以画到一半先保存。
运行的是从流程图推出来的树,所以图上画的必须就是实际运行的。发布时还会检查图形:
- 每个节点至少 10 × 10,节点之间不能重叠;
- 每条连线至少有两个不重合的点,起点落在它的来源节点上,终点落在它的目标节点上;
- 连线不能穿过或贴近(5 px 以内)其他节点;
- 元素不能带自定义颜色。
连线穿过了别的节点
设计器默认画直线。直线穿过别的节点时会被标红,把这条线改画成绕开节点的折线就可以了。
导入和导出
- 设计页的"导出 BPMN"导出画布上现在的流程图,包括还没保存的修改;版本列表的"导出 BPMN"导出所选版本。导出的文件带着节点设置;
- 设计页的"导入"接受 .bpmn 和 .xml 文件,以及从版本列表导出的流程 JSON。所以树形模型可以先导出 JSON,再导入到 BPMN 模型里,反过来不行。导入会替换画布上的流程图,导入后还没有保存,检查结果马上显示,确认无误再保存草稿或发布;
- BPMN 模型的版本列表没有"导入 JSON";
- 只支持上面这些元素。其他 BPMN 工具导出的文件通常带表达式、监听器、专有扩展、泳池或子流程,这些内容不改就发布不了。导入后检查结果马上显示:问题列在画布上方,对应的元素标红,点一下就能定位。在画布上删掉这些内容(或者先在原来的工具里去掉)再发布,必要时按原来的流程重新画。
已经保存的草稿如果画不出来,设计页会改为显示只有开始事件的新流程图,并提示导入 .bpmn 文件或重新绘制。保存草稿后会替换原来的草稿。
节点类型
| 节点 | 作用 |
|---|---|
| 发起 | 发起人,可以设置表单字段权限 |
| 审批 | 指定审批人、多人审批方式、驳回策略、超时提醒和到期后的自动处理 |
| 抄送 | 通知相关人员,不需要处理 |
| 分支 | 条件分支、并行分支、包容分支 |
审批人
可以指定为:具体用户、角色、岗位、部门成员、部门负责人、多级部门负责人、发起人本人、发起人自选、发起人的部门负责人、表单中某个用户字段、表单中某个部门字段的负责人。
多人审批方式:或签(一人通过即可)、会签(全部通过)、依次审批。
特殊情况:找不到审批人时,可以自动通过、转交流程管理员或转给指定的人;审批人就是发起人时,可以由本人审批、跳过或者转给部门负责人。
分支
- 条件分支:按顺序找到第一条满足条件的路径,都不满足时走默认路径;
- 包容分支:满足条件的路径全部都走;
- 并行分支:所有路径同时进行,全部完成后再汇合。
条件可以使用表单字段(等于、大于、包含……),也可以使用发起人的部门(包括下级部门)和角色。
审批动作
通过、驳回、退回(到走过的任一节点,或者退回给发起人修改后重新提交)、转办、委派、前加签和后加签、减签、抄送、撤回(刚通过但下一个人还没处理时)、撤销(发起人撤销整个流程)、评论、催办(每小时最多一次)。
管理员还可以终止流程实例,或者把任务改派给其他人(比如员工离职交接时)。
超时自动处理
审批节点打开"超时提醒"后,填写"办理时限(小时)",可以选"之后每隔(小时)重复提醒",还可以选到期后怎么办:
| 到期后 | 系统做什么 |
|---|---|
| 仅提醒(默认) | 只提醒办理人,不会自动通过或驳回 |
| 自动通过 | 以办理人的名义通过。会签时每个人的待办各自到期、各自处理;依次审批时,下一位从这时开始计时 |
| 自动驳回 | 以办理人的名义驳回,然后按本节点的"驳回后"设置:结束流程,或者退回上一审批节点 |
| 转给上级 | 把待办转给办理人的上级,新待办重新计时 |
转给上级找谁:
- 办理人所在部门的负责人;办理人自己就是负责人时,找上级部门的负责人;
- 找不到(比如没有部门、部门没有负责人、负责人已停用),或者这个人本轮已经在这个节点经手过,或者他是发起人而节点不允许发起人自己审批,就按名单顺序找第一位合适的、启用的流程管理员;
- 还找不到,就只提醒办理人,并记在审批记录里。
新办理人再超时,按同样的规则转给他的上级。每次都只转给本轮没经手过的人,所以不会来回转。或签节点上,如果上级或流程管理员已经有这个节点的待办,就只提醒,不再拉别人进来。
两个"转给"不一样
这里的"转给上级"找的是办理人的上级。审批人里"找不到审批人"时的"转交流程管理员",找的是这个模型的流程管理员。
什么时候处理:由内置的定时任务"发送待办超时提醒"处理,它每 5 分钟运行一次,所以到期后最多晚 5 分钟左右(见定时任务 · 内置任务)。
- 每条待办只处理一次。和人工操作同时发生时也一样:人先处理了,系统就跳过;系统先处理了,人的操作会失败。部署多个实例时也不会重复处理;
- 只处理办理人自己的待办。委派或加签过的待办,以及委派、加签产生的待办,到期后都只提醒;
- 处理过一次之后(包括转给上级时没有转出去、只提醒了办理人,以及处理失败),只按普通提醒继续,不再自动处理;
- 设置了自动处理的节点上,转办、改派、撤回后重新出现的待办,从这时起重新计时;
- 自动通过的审批不能撤回,详情页也不显示"撤回";
- 自动驳回退回上一审批节点时,会跳过本轮被自动通过的节点,都被跳过就退回发起人,所以自动通过和自动驳回不会来回循环。
不会悄悄发生:
- 每次处理都在审批记录里写一条"超时处理",操作人显示为"系统",意见写明是谁的待办、系统做了什么,比如"张三 的待办:超时未处理,系统已自动通过";
- 发起人和这个模型启用的流程管理员会收到"流程待办超时处理通知"(站内信和邮件)。自动通过、自动驳回或转交时,原办理人也会收到;新办理人照常收到新待办通知;
- 已办列表不把系统的处理算在原办理人名下。
处理失败:比如自动通过后,下一个审批节点找不到审批人。这时这次处理整个撤销,审批记录里写一条处理失败,办理人收到超时提醒,发起人和流程管理员收到通知。修好流程或组织架构后,在 流程审批 → 流程管理 → 任务管理 里改派一次,待办会重新计时、重新进入自动处理。
流程管理员需要的权限
流程管理员只是收通知的人,本身不能查看或改派流程实例。要让他们处理这类通知,还要给他们的角色勾选 实例管理 的"浏览"和它下面"审批详情"的"查看"、任务管理 的"浏览"和"改派与终止",并且数据范围要覆盖发起人的部门。
查看进度
流程实例的详情页有一块流程进度,节点按状态着色,并附有图例:已完成、进行中、未到达、未经过、在此结束。并行分支的几条路径会同时着色。
- 树形模型显示一棵只读的进度树,形状和设计器里一样;
- BPMN 模型显示只读的流程图,可以拖动和缩放,着色和进度树用的是同一份数据。
如果某个流程版本因为规则变化已经通不过检查,详情页照常打开,只是不显示流程进度,审批记录照常显示。
移动端的审批详情不显示流程进度(没有进度树,也没有流程图),审批记录照常显示。
通知
新待办、通过、驳回、抄送、退回、催办、超时提醒、超时处理等事件,会通过站内信、邮件和实时推送通知相关人员,内容按收件人的语言发送。
两种表单
- 业务表单:你自己写的业务页面。实现一个业务处理器,告诉引擎有哪些字段可以用作分支条件、发起前要检查什么、流程结束后更新什么。自带的请假示例(菜单 流程审批 → 请假申请)就是一个完整的业务表单接入示例,源码在服务端
modules/biz/leave目录; - 动态表单:用表单设计器拖拽生成,不需要写代码,见下一节。
动态表单
表单在 流程审批 → 流程管理 → 表单管理 中设计和保存(也可以直接用新建审批向导)。然后在模型管理里给模型选"绑定表单",发布时系统从表单推导出字段清单,和表单一起存进这个版本。之后再修改表单,要重新发布才会生效;已经发起的流程不受影响。被模型绑定的表单不能删除。
字段权限
在流程设计器的"发起人"和每个审批节点上,可以给每个字段设置可编辑、只读或隐藏(没设置的按只读处理):
- 发起时所有字段都可以填写;
- 审批详情按读者的字段权限显示。隐藏的字段连同它的值都不会发给浏览器;只有当前待办节点设为"可编辑"的字段能修改,修改随"通过"一起提交;
- 某个字段曾经对某人隐藏,之后也一直对他隐藏(办完了也看不到);
- 被退回后重新提交时,发起人只能修改"发起人"节点上设为"可编辑"的字段;
- 抄送节点没有字段权限:抄送节点抄送的人,以及有实例查看权限的管理员,看到全部字段(只读);但他们自己也是这个流程的发起人或审批人时,仍按自己节点的权限看。只是被某个审批人手动抄送进来的人,按发出抄送的那个节点的权限看。
表单里的人和部门
审批人可以指定为"表单中的人员"(取人员选择字段里选的那个人),或者"表单中部门的负责人"(取部门选择字段所选部门的负责人)。这个人已停用时会被跳过,找不到人时按"找不到审批人"的规则处理。
附件
附件字段上传的是私有文件,发起时绑定到这个流程实例,审批时修改、重新提交时新加的附件也会绑定。只能使用本人上传、还没被用过的文件。
能下载附件的人:能查看这个流程实例,并且能看到这个附件字段的人(字段对他隐藏时就不能下载)。上传人本人、超级管理员,以及有 系统管理 → 文件管理 → 文件列表 "查看"权限(storage.object.view)的人始终可以下载;文件被从表单里移除后,只有他们能下载。
服务端校验
浏览器里的校验可以被绕过,所以服务端会按每个组件的取值格式再校验一次,发起、审批修改和重新提交都一样:
- 必填、选项(单选、下拉、多选的值必须在选项里)、字典(必须是当前启用的字典项);
- 数字范围、评分和滑块的范围、文字长度(没有设置时最多 5000 字)、附件数量、多选的最多数量;
- 单个日期和日期时间必须是 ISO 格式(日期时间要带时区);月、年、周、多个日期和时间只当作文字,不检查格式;
- 天数和明细合计由服务端重新计算,覆盖浏览器提交的值。分支条件按重新计算后的值走。
手机上的动态表单
移动端也能填写和查看动态表单。字段权限、提交哪些字段、服务端的校验都和电脑端相同;和电脑端不同的地方(附件、少数组件、字段上另外设置的校验规则)在下面和本页的"已知限制"里说明:
- 发起:在移动端 工作台 → 发起审批 里点一个动态表单的流程。绑定了表单的,打开表单页填写,有"发起人自选"的节点时,选人也在这一页;没有绑定表单的,在底部弹层里发起(有"发起人自选"的节点时在这里选人);
- 审批详情:表单直接显示在详情里,按你所在节点的字段权限显示:隐藏的字段看不到,"可编辑"的字段可以修改,其余只读;
- 通过和重新提交:只提交"可编辑"的字段;提交前先在手机上按服务端的规则检查一遍(必填、类型、选项、范围、长度、日期格式等);
- 自动计算的值(天数、明细合计)只用来显示,以服务端重新计算的结果为准;
- 明细表:每行一张卡片,可以"添加明细"、删除一行,下面显示合计;
- 附件:可以上传,已有的附件只显示文件名,在手机上不能打开或下载。
有些组件在手机上没法填写,这些字段只读,并提示「请在电脑端填写」:选择多个日期、按周、按多年或多月选择的日期,值格式里用了年、月、日、时、分、秒和时区以外写法的日期,多选的级联选择,以及设计器自带的"上传"组件(附件请用"附件"组件,见表单设计器)。整张表单在手机上显示不出来时,发起页提示「该表单无法在手机上显示,请在电脑端发起」,审批详情的表单一栏显示「请在电脑端查看」,但仍可以通过、驳回。
哪些字段能做分支条件
数字(计数器、评分、滑块、天数、明细合计)、日期、人员、部门,以及取值是一段文字的字段,可以用作条件。多选、级联、地区、日期区间、开关,以及选项值是数字的单选和下拉,任何条件都不会命中,流程会走默认路径。
已知限制
- 服务端还没有校验:数字的步长和精度、评分的半星、级联的值是否在选项里、多选的最少数量、区间里的日期;
- 评分和普通滑块按数字处理,可以比较大小(范围滑块的值是一组数,任何条件都不会命中);月、年、周的日期按文字处理,只能用等于、不等于、属于、包含,不能按日期先后比较;
- 明细表被隐藏、合计字段可见时,合计不显示;
- 移动端:少数组件只读(提示「请在电脑端填写」),附件不能打开或下载,见手机上的动态表单。字段上另外设置的校验规则(正则、邮箱等,必填除外)只有电脑端的表单会检查,手机上不检查,服务端也不检查。
审批数据
在 流程审批 → 流程管理 → 审批数据 中,可以按某一个审批查看它的所有申请,表单的每个字段就是一列:
- 固定列有编号、状态、发起人、发起部门、发起时间、结束时间,后面是所选版本表单的字段;
- 可以按状态、发起人、发起时间筛选,也可以按字段值筛选(最多 10 条),比如"请假天数大于 3"。能用的比较方式取决于字段类型;
- 切换版本只改变显示的列和能筛选的字段,列表里仍然是这个审批所有版本的申请;
- 可以导出 Excel,导出的列、筛选和排序和页面一致,并记录到操作日志;
- 人员和部门字段显示名称。字典字段显示保存的值(比如
annual),不翻译成字典标签;明细表显示为 JSON 文本;附件显示为保存的原始文本,每个文件是一条"文件编号/文件名"(比如12/合同.pdf)。
权限是 wf.data.browse(浏览)和 wf.data.export(导出),默认只授给超级管理员。查到的数据还要按发起人所在部门做数据范围过滤。
审批数据里的字段权限
审批数据按字段权限过滤:
- 超级管理员和这个审批的流程管理员(在模型管理的编辑弹框或向导的"高级设置"里设置)看到全部字段,弹框里"流程管理员"的说明也写明了这一点;
- 其他有
wf.data.browse/wf.data.export的人,看不到在任何节点(包括发起人节点)上设为"隐藏"的字段:页面和导出都没有这一列,按它筛选会和"字段不存在"一样报错。页面顶部会提示"有 N 个字段在流程中被隐藏,仅流程管理员可见"; - 每一行按它自己的版本判断:某个字段在这一行的版本里是隐藏的,这一行就不返回它的值,按这个字段筛选时也不会命中这一行。这样就不能切换到一个没隐藏该字段的旧版本,去查新版本里这个字段的数据;
- 某个版本的流程已经通不过检查时,这个版本的所有字段都按隐藏处理。
因为流程管理员能看到全部字段,修改流程管理员名单需要单独的权限:模型管理 下的"任命流程管理员"(wf.model.managers),默认只有超级管理员有。只有"修改"权限的人,在模型管理的编辑弹框和向导的"高级设置"里,"流程管理员"这一项是只读的,并提示"你没有修改流程管理员的权限(wf.model.managers),此项保持不变。"绕过界面直接调用接口修改名单,会返回 403。
所以,要让部门经理这类角色查看审批数据,在 系统管理 → 角色管理 里授予 wf.data.browse(需要导出再加 wf.data.export)就可以了,他按角色的数据范围看到申请(见权限 · 例子);需要看敏感字段的人(比如 HR),请有"任命流程管理员"权限的人把他设为这个审批的流程管理员。
注意
字段权限按单个字段计算,能看到的字段可能间接透露隐藏的字段。比如隐藏了"请假天数",但开始日期和结束日期可见。
安全
- 只有发起人、办理人、抄送人和有权限的管理员才能查看流程实例,其他人访问一律返回 404;
- 只能处理分配给自己的任务;
- 表单字段权限(可编辑、只读、隐藏)由服务端强制执行,审批详情里隐藏的字段根本不会发送给前端,审批数据页面也按字段权限过滤(流程管理员除外,任命流程管理员需要单独的权限);附件也只对能看到该字段的人开放下载(上传人本人、超级管理员和有文件列表"查看"权限的人除外);
- 动态表单的结构保存时经过安全白名单检查,不能包含函数、脚本和事件设置,见表单设计器 · 安全;
- BPMN 流程图只当作数据:脚本、表达式、监听器、其他工具的扩展和 DOCTYPE 一律拒绝,大小有上限。服务端只运行它自己从流程图推出来的树,见发布时的检查。