工作流 Flowable 全流程


一,管理员创建流程分类

在正式设计复杂的业务流程之前,我们需要先为它们建立一个"文件夹体系"。流程分类(Category)的作用就是对不同业务域(如人事、财务、行政)的流程进行逻辑隔离与归纳。

1. 业务交互流程

这一步是整个 BPM 系统的入口。管理员的操作路径非常直观:

  • 操作入口:登录后台管理系统,进入【工作流】->【流程分类】菜单。
  • 输入信息:点击"新增"按钮,填写分类的基础元数据。
  • 分类名称 (name) :展示给用户看的名字,例如 Test03(或"人事考勤")。
  • 分类编码 (code) :系统内部识别的唯一标识,例如 Test03(或 hr_attendance)。
  • 排序 (sort) :决定该分类在列表中的显示顺序,例如 3
2. 前后端数据流转

当管理员点击"确定"后,数据在系统内部经历了一次标准的 CRUD:

  • 前端请求 (Request)
    前端封装表单数据,发起 POST 请求至 /bpm/category/create 接口。
json 复制代码
// 请求载荷示例
{
  "name": "Test03",
  "code": "Test03",
  "sort": 3
}
  • 后端处理 (Processing)

    Controller 层接收请求后,下沉至 Service 层进行核心校验。

  • 唯一性检查 :这是最关键的业务规则。系统会查询 bpm_category 表,确保传入的 namecode 在全表中是唯一的。如果已存在,直接抛出异常,防止数据冲突。

  • 持久化:校验通过后,后端将数据封装为 Entity 对象。

  • 数据库落地 (Database)

    最终,一条新的记录被插入到业务表 bpm_category 中。

注意 :bpm_category是 业务扩展表,而非 Flowable 的原生表。Flowable 虽然也有 Category 字段,但为了实现更复杂的层级管理和元数据控制,我们通常维护一张独立的分类表。


二,管理员创建流程表单

在定义了流程分类后,下一步是构建流程流转中承载数据的容器------流程表单。本环节通过低代码形式,将业务需求转化为系统可识别的数据结构。

1. 可视化表单设计

管理员进入后台管理系统,通过内置的表单设计器进行可视化配置。在此场景中,管理员创建了一个名为"请假表单"的业务单据,并拖拽配置了两个核心组件:

  • 请假原因:单行文本输入框。
  • 请假时间 :单行文本输入框(用于输入具体天数)。
    完成布局与属性配置后,点击保存触发提交。
2. 前后端交互协议

前端将可视化的配置结果转化为 JSON 数据结构,并通过 POST 请求发送至 /bpm/form/create 接口。请求载荷包含三个核心参数:

  • name (表单名称)

    标识表单的业务名称,此处为 "请假表单"

  • conf (全局配置)

    定义表单的整体样式与行为。

    • 样式参数 :如 "labelPosition": "right"(标签右对齐)、"labelWidth": "100px"(标签宽度)。
    • 按钮控制 :定义了 "submitBtn"(提交按钮)显示,而 "resetBtn"(重置按钮)隐藏。
  • fields (组件字段定义)

    这是表单的核心数据模型,以数组形式定义了具体控件的属性。

    • 组件一(请假原因) :字段标识为 field: "Fkkgmkfa5l8eabc",类型为 input,标题为 title: "请假原因"
    • 组件二(请假时间) :字段标识为 field: "Fl6ymkfbx7rrahc",类型为 input,标题为 title: "请假时间"

注意 :前端生成的随机字符串 ID(如 Fkkg...)是后续流程运行时存取具体业务值的唯一键(Key)。

3. 数据持久化存储

后端接收请求后,解析并校验参数,随后执行插入操作:

  • 目标表bpm_form(业务表单定义表)。

  • 存储逻辑

    • conffields 字段被序列化为 JSON 字符串 存储在对应的数据库列中。
    • 系统为该表单生成一个全局唯一的 id
  • 返回结果:返回创建成功状态。

此步骤生成的 formId 至关重要,它将在下一章"创建流程模型"时被引用,从而实现"流程定义"与"业务表单"的物理绑定。


三,管理员创建流程模型

在完成了分类与表单的创建后,核心环节便是构建流程模型。此步骤旨在定义业务流转的逻辑规则(BPMN 图),并将前两章创建的分类与表单进行物理绑定。

1. 模型配置与可视化设计

管理员在后台管理系统中新建流程模型,操作分为两个维度:

  • 元数据配置

    • 基本信息 :定义流程名称为 请假流程,唯一标识 Key 设为 leave,并归属到 Test03 分类下。
    • 表单绑定 :选择上一章创建的"请假表单",系统在后端记录该表单的唯一标识 formId=41
  • BPMN 图形设计

    • 进入流程设计器(Modeler),绘制标准的 BPMN 2.0 流程图。
    • 配置起始节点(StartEvent),确保其关联了上述请假表单(用于发起流程时渲染)。
    • 绘制结束节点(EndEvent)以构成闭环,并点击保存。
2. 数据传输协议

前端将配置元数据与图形数据合并,通过 POST 请求发送至 /bpm/model/create 接口。核心载荷(Payload)包含:

  • 元数据name="请假流程", key="leave", category="Test03", formId=41。
  • 图形数据bpmnXml(由设计器生成的标准 XML 字符串)。
3. 后端处理与双表存储

后端接收请求后,首先校验模型 Key 的合法性及唯一性。校验通过后,通过 Flowable 原生的 RepositoryService 执行两步核心存储操作,分别涉及模型元数据表通用字节资源表

  • 步骤一:存储模型元数据 (ACT_RE_MODEL)

    调用 repositoryService.saveModel(model) 初始化模型记录。

    • KEY_ / NAME_ / CATEGORY_:直接映射传入的基本信息。
    • META_INFO_ (关键扩展) :由于 Flowable 原生模型表没有 formId 字段,框架将 formId: 41 以及描述信息封装为 JSON 字符串,存储在此字段中。这是实现"流程-表单"绑定的关键。
  • 步骤二:存储模型源文件 (ACT_GE_BYTEARRAY)

    调用 repositoryService.addModelEditorSource(id, bytes) 处理 BPMN XML 数据。

    • 数据转换 :将前端传来的 bpmnXml 字符串转换为 UTF-8 字节流。
    • 持久化 :将字节流作为一条记录插入 ACT_GE_BYTEARRAY 表。此表专门用于存储二进制大对象(BLOB)。
    • 建立关联 :Flowable 自动将新生成的字节记录 ID 回填至 ACT_RE_MODEL 表的 EDITOR_SOURCE_VALUE_ID_ 字段。

这是为您总结的第四章内容。这一章是流程设计走向流程运行的分水岭,技术密度较高。

我对您提供的步骤进行了核心优化,特别是在数据隔离 (草稿 vs 定稿)和扩展元数据存储方面做了更严谨的技术表述。


四,版本化与发布------管理员部署流程模型

流程模型在"部署"之前,仅仅是存储在数据库中的静态设计草稿。部署操作是将设计草稿转化为引擎可执行代码(ProcessDefinition)的关键步骤,同时涉及严格的版本控制与数据快照机制。

1. 部署触发与完整性校验

管理员在流程模型列表中选择"请假流程",点击【发布】按钮。

  • 接口交互 :前端通过 POST 请求调用 /bpm/model/deploy 接口,仅需传输模型主键 id
  • 前置校验 :后端根据 id 查询 ACT_RE_MODEL 表获取模型元数据及 XML 源码。系统调用 Flowable 校验器(BPMN Validator)对 XML 进行语法分析,检查是否存在游离节点、缺失的起始/结束事件或不合法的连线逻辑。
2. 核心部署:Flowable 引擎的三表联动

校验通过后,后端调用 Flowable 原生接口 repositoryService.createDeployment()...deploy() 执行部署。这是一个原子性事务,底层数据库发生如下级联变化:

  • 生成部署档案 (ACT_RE_DEPLOYMENT)
    插入一条新记录,生成全局唯一的 DEPLOYMENT_ID_。该记录标识了本次发布的版本快照时间与名称。
  • 固化资源文件 (ACT_GE_BYTEARRAY)
    引擎将模型草稿中的 XML 源码复制 一份,作为不可变的资源文件插入此表,并将 DEPLOYMENT_ID_ 关联到上一条记录。

技术修正:此处实现了"设计"与"运行"的物理隔离。即便后续管理员修改了模型草稿(Model),也不会影响已部署运行的资源文件。

  • 生成流程定义 (ACT_RE_PROCDEF)
    引擎解析 XML 结构,提取流程规则,插入一条流程定义记录。
  • 版本控制 :引擎自动查询该 Key(leave)的历史版本,将 VERSION_ 字段设为"最大版本号 + 1"。
  • 状态关联 :该记录同时关联上述的 DEPLOYMENT_ID_ 和资源文件名称。
3. 扩展元数据存储 (bpm_process_definition_info)

由于 Flowable 原生的 ACT_RE_PROCDEF 表结构固定,无法存储业务框架特有的配置(如关联的 formId、自定义图标、描述等)。

系统在此步执行扩展逻辑:从 ACT_RE_MODELMETA_INFO_ 中提取表单 ID 等信息,结合新生成的 processDefinitionId,插入到自定义扩展表 bpm_process_definition_info 中。这确保了流程定义与业务表单在运行时的强绑定。

4. 模型状态回写

部署的最后一步是更新设计草稿的状态。系统将新生成的 deploymentId 回写到 ACT_RE_MODEL 表的 DEPLOYMENT_ID_ 字段。

  • 字段非空:表示该模型已发布,且指向最新的部署版本。
  • 逻辑闭环:至此,流程模型完成了从"设计态"到"运行态"的转化,具备了发起流程实例的能力。
5. 深入解析:设计态与运行态的资源隔离(ACT_GE_BYTEARRAY 实例分析)

在执行部署操作后,若观察底层的通用字节资源表 ACT_GE_BYTEARRAY,会发现同一个"请假流程"存在两类截然不同的 XML 资源记录。这并非数据冗余,而是 Flowable 架构中为了实现"设计态"与"运行态"物理隔离而设计的核心机制。

我们将第三章(创建模型)与第四章(部署模型)产生的两条关键记录进行对比分析(参考数据库实例数据):

第一类记录:设计态源文件(Editor Source)

  • 来源 :产生于第三章"创建流程模型" ,对应 repositoryService.saveModel 操作。

  • 实例特征

    • NAME_ :固定为 source。这是 Flowable 设计器专用的标识,代表这是"设计草稿"。
    • DEPLOYMENT_ID_(Null)。关键特征。因为它是草稿,尚未发布,所以不关联任何部署记录。
    • REV_7(示例值)。高版本号表明管理员在设计器中点击了 7 次保存。
  • 技术含义 :这条记录是可变的。它仅供前端流程设计器(Model Editor)回显和编辑使用。无论管理员如何修改这条记录,都不会影响线上正在运行的业务。

第二类记录:运行态定义文件(Deployment Resource)

  • 来源 :产生于第四章"部署流程模型" ,对应 repositoryService.deploy 操作。

  • 实例特征

    • NAME_ :变更为具体的资源名称 leave.bpmn (格式通常为 流程Key + .bpmn)。
    • DEPLOYMENT_ID_7e7559db... (示例值)。有具体值,指向 ACT_RE_DEPLOYMENT 表的一条部署记录。
    • REV_1。永远为 1。
  • 技术含义 :这条记录是不可变的快照 。部署动作发生时,引擎读取了上述的 source 草稿,将其转化为标准的 BPMN 2.0 XML 格式,并复制 一份作为永久归档插入此表。流程引擎(Process Engine)在流转任务时,只读取这条记录,完全忽略 source 记录。


五,实例初始化------员工发起流程

在流程模型部署上线后,业务流转的实质性阶段始于"流程实例(Process Instance)"的创建。本章阐述员工提交申请时,系统如何将静态的流程定义转化为动态的运行数据。

1. 业务交互与数据封装

员工在后台管理系统前端访问已部署的"请假流程"入口。

  • 表单渲染 :前端根据第二章定义的 JSON 配置渲染动态表单,员工填写业务数据:请假原因="看病",请假时间="3"。
  • 提交动作:点击"发起"按钮,触发提交逻辑。
2. 接口协议与载荷定义

前端封装请求参数,发送 POST 请求至 /bpm/process-instance/create。请求载荷(Payload)包含两个核心要素:

  • processDefinitionId :指向具体的流程定义版本 ID(如 leave:1:7e82...),确保基于正确的版本发起。
  • variables:将表单业务数据封装为键值对 Map。
json 复制代码
{
  "Fkkgmkfa...": "看病", // Key为表单设计时生成的随机字段ID
  "Fl6ymkfb...": "3"
}
3. 后端预处理(业务层)

后端 Service 层接收请求后,在调用引擎之前执行一系列业务校验与数据增强:

  • 定义校验 :根据 processDefinitionId 查询 ACT_RE_PROCDEF 表,确认流程定义处于激活(Active)状态。
  • 权限与合法性校验:验证发起用户是否具备该流程的发起权限,并校验传入参数的格式合法性。
  • 上下文增强 :向 variables 中注入系统级内置变量,例如:
    • PROCESS_START_USER_ID: 发起人 ID。
    • PROCESS_STATUS: 初始化为"审批中"。
4. 引擎核心执行(持久化层)

业务层构建 ProcessInstanceBuilder 并调用 .start() 方法。此操作是一个原子性事务,触发 Flowable 引擎执行节点流转多表级联写入

关键数据库操作:

  • A. 初始化执行流 (ACT_RU_EXECUTION)

    引擎并非只插入一条记录,通常会生成两条记录以构建执行树:

    • 根执行流 (Root Execution) :代表流程实例本身。ID_PROC_INST_ID_ 相同,PARENT_ID_ 为空。
    • 子执行流 (Child Execution) :代表当前的执行指针。PARENT_ID_ 指向根执行流 ID,ACT_ID_ 指向当前停留的节点(如第一个用户任务)。
  • B. 变量持久化 (ACT_RU_VARIABLE)

    将增强后的 variables Map 扁平化存储。每一条键值对对应表中一行记录,外键 PROC_INST_ID_ 关联上述的根执行流 ID。

  • C. 身份链路绑定 (ACT_RU_IDENTITYLINK)

    引擎会向此表插入一条记录,标记当前用户为流程的 starter(发起人)。这对于后续查询"我发起的流程"至关重要。

  • D. 任务生成 (ACT_RU_TASK)

    引擎计算流程图路径,从 StartEvent 流转至第一个节点(假设为用户任务)。

    • 在表中插入一条新任务记录。
      • ASSIGNEE_ (办理人) :根据流程图配置(如 ${initiator} 或固定值)解析并填入具体用户 ID。
      • EXECUTION_ID_ :关联到上述的子执行流 ID(注意:是关联子执行流,而非根实例)。
  • E. 历史审计同步 (ACT_HI_*)
    start() 不仅操作运行时表(Runtime),会同步写入历史表以保证审计可追溯:

    • ACT_HI_PROCINST:记录流程实例开始时间。
    • ACT_HI_ACTINST:记录"开始节点"已完成,"用户任务节点"已开始。
    • ACT_HI_TASKINST:记录生成的第一个任务的历史存根。
    • ACT_HI_VARINST:记录变量的历史快照。

start() 操作标志着流程实例的正式诞生。此时,数据库中形成了完整的运行时数据结构(Execution Tree + Variables + Task),且首个审批节点的办理人已能在其"待办任务"列表中查询到该记录。


六,领导查看待办列表

流程实例发起后,任务流转至审批节点。此时,系统需要精准地将"任务"分发到对应审批人的工作台中。本章阐述系统如何通过身份上下文,从引擎中检索出属于当前用户的活跃任务。

1. 交互入口与请求上下文

领导一登录后台管理系统,进入【我的待办】菜单。

  • 接口协议 :前端发起 GET 请求至 /bpm/task/todo-page 接口。
  • 身份凭证 :请求头(Header)中携带了 Authorization: Bearer {token}。这是后端识别"当前是谁在查任务"的唯一凭证,避免了在 URL 参数中显式传递 UserID 带来的安全隐患。
2. 后端上下文解析

后端接口接收请求后,并非直接调用引擎,而是先进行上下文处理:

  • 身份提取 :通过安全框架(如 Spring Security)解析 Token,从 SecurityContextHolder 中获取当前登录用户的 ID(例如 User_Leader)。
  • 分页参数 :接收前端传递的分页参数(如 pageNo=1, pageSize=10),以便应对大量待办数据的场景。
3. 引擎查询与底层 SQL 构建

后端 Service 层构建 Flowable 的 TaskQuery 对象,执行核心查询逻辑。代码层面的 taskService.createTaskQuery()... 会被引擎翻译为复杂的 SQL 语句,涉及对 运行时任务表 (ACT_RU_TASK)运行时变量表 (ACT_RU_VARIABLE) 的联合操作。

关键查询链式调用如下:

  • taskAssignee(userId)

    • SQL 映射WHERE ASSIGNEE_ = 'User_Leader'
    • 含义:精准匹配分配给当前用户的任务。
    • (注:实际业务中常用 taskCandidateOrAssigned 以涵盖"候选人"模式,但此处按您描述的直接指派模式,使用 assignee 即可)
  • active()

    • SQL 映射AND SUSPENSION_STATE_ = 1
    • 含义:仅查询激活状态的任务,过滤掉被挂起(Suspended)的任务。
  • includeProcessVariables()

    • SQL 映射LEFT JOIN ACT_RU_VARIABLE ...
    • 含义:这是一个关键的性能优化操作。它指示引擎在查询任务的同时,通过**预加载(Eager Loading)**的方式一次性拉取关联的流程变量(如请假原因、请假天数)。这避免了后续遍历任务列表时产生 "N+1" 次查询数据库的问题。
  • orderByTaskCreateTime().desc()

    • SQL 映射ORDER BY CREATE_TIME_ DESC
    • 含义:按任务创建时间倒序排列,确保最新的待办显示在最前。
4. 数据转换与响应

引擎返回查询结果(List<Task>)后,后端不会直接将 Flowable 的原生实体返回给前端(因为包含大量引擎内部字段),而是进行对象转换(DTO/VO):

  • 基础映射:提取 Task ID, Name, CreateTime 等基础字段。
  • 变量注入 :从查询结果中提取 processVariables(如 {"Fkkg...": "看病", "Fl6y...": "3"}),并将其映射为前端可读的业务字段。
  • 流程定义关联 :通常还会根据 PROC_DEF_ID_ 关联查询流程定义名称("请假流程"),以便用户知道这是什么流程的任务。

最终,前端接收到包含业务数据的标准分页列表,渲染出领导看到的待办表格。


第七章:流转与驱动------领导审批待办任务

当领导对待办任务进行"通过"操作时,系统不仅要处理业务层面的审批意见与签名,还需驱动 Flowable 引擎完成核心的"节点跃迁":销毁当前任务,移动执行指针,并生成下一个任务节点。

1. 审批动作与接口协议

领导在任务详情页点击【通过】按钮,若业务场景配置了签名或审批意见,需同时提交相关数据。

  • 接口调用 :前端发送 PUT 请求至 /bpm/task/approve
  • 载荷参数
  • id: 任务唯一标识(如 d7d3cb54-f212...)。
  • reason: 审批意见(可选)。
  • signatureImage: 电子签名图片(取决于配置)。
  • nextAssignee: 指定的下一节点审批人(若流程配置为手动选择)。
2. 动态规则校验(基于 BPMN 扩展属性)

后端接收请求后,首先进行基础校验(任务是否存在、流程实例是否活跃)。随后,进入基于模型的动态校验逻辑

  • 获取节点定义 :根据任务的 taskDefinitionKey,从内存缓存的 BpmnModel 中定位到对应的 FlowElement 对象。
  • 解析扩展属性 :读取该节点配置的 extensionElements,检查是否存在自定义标签 signEnable
  • 强制校验 :若 signEnable=true,系统强制检查请求参数中是否包含 signatureImage。若缺失,直接抛出业务异常,拦截后续操作。这一机制实现了"配置驱动校验",无需硬编码。
3. 上下文准备:预设下一审批人

在触发流转前,系统需解决"下一棒交给谁"的问题。

  • 推理与设置 :根据前端传递的 nextAssignee 或业务规则,确定下一节点的受托人。
  • 变量持久化 :调用 runtimeService.setVariables(),将目标用户 ID 写入 ACT_RU_VARIABLE 表。
  • 目的 :Flowable 引擎在创建下一个任务时,会通过 UEL 表达式(如 ${approver})读取此变量作为新任务的 Assignee。
4. 引擎核心流转 (taskService.complete)

校验与参数准备完成后,调用 taskService.complete(taskId)。这是一个高度封装的原子操作,底层数据库发生剧烈的级联变化:

  • A. 任务销毁与归档 (Current Task)

    • 物理删除 :执行 DELETE 操作,从 ACT_RU_TASK 表中移除当前领导的待办记录。
    • 历史审计同步更新 ACT_HI_TASKINST 表,标记该任务状态为 completed,并记录 END_TIME_(结束时间)与 DURATION_(任务耗时)。
  • B. 路径计算与指针移动 (Navigation)

    • 读取模型:引擎从 Deployment Cache 中读取流程定义模型(无需每次查询数据库 XML),根据当前节点的"出口连线"计算目标节点。
    • 移动指针 :更新 ACT_RU_EXECUTION 表。
    • 将当前执行流的 ACT_ID_ 字段由"领导审批节点 ID"更新为"下一个节点 ID"。
    • (注:若遭遇并行网关,此处会涉及执行流的分裂或聚合操作)
  • C. 下一任务生成 (Next Task)

    • 节点实例化 :若目标节点仍为 UserTask,引擎向 ACT_RU_TASK 表执行 INSERT 操作,生成一条新的待办记录。
    • 属性填充 :新记录的 TASK_DEF_KEY_ 指向新节点 ID,ASSIGNEE_ 字段根据第 3 步预设的变量自动填充。
    • 历史同步 :同时向 ACT_HI_TASKINST 插入一条新记录(无结束时间),标志着下一环节审计追踪的开始。
相关推荐
骄马之死18 小时前
SpringMVC + SpringBoot 核心知识点总结
java·spring boot·后端
GoGeekBaird19 小时前
Anthropic技能"(Skills)的经验分享
后端
王码码203519 小时前
多台服务器怎么统一看状态?Beszel 轻量监控,搭起来不费事
运维·服务器·后端·安全·阿里云·接口·web
郑洁文19 小时前
基于Spring Boot的流浪动物救助网站
java·spring boot·后端·毕设·流浪动物救助
螺丝钉code20 小时前
JAVA项目 Claude code CLAUDE.md 到底应该怎么写
java·人工智能·claude code
指令集梦境21 小时前
Cursor + Spring Boot实战:从零写一个RESTful API
spring boot·后端·restful
摇滚侠21 小时前
Maven 入门+高深 单一架构案例 54-59
java·架构·maven·intellij-idea
VidDown1 天前
Webhook 调试器:让第三方回调“原形毕露”
java·开发语言·javascript·编辑器·postman
码云之上1 天前
聊聊如何设计一个高效、稳定的 Node.js 接入层
前端·后端·node.js
折哥的程序人生 · 物流技术专研1 天前
Java 23 种设计模式:从踩坑到精通 | 原型模式 —— 克隆对象,深拷贝与浅拷贝的坑你踩过吗?
java·设计模式·架构·原型模式·单一职责原则