# scaffold 项目之工作流
# 简介
Flowable 是一个基于 Java 的开源轻量级业务流程管理(BPM)平台,用于实现业务流程的自动化、管理和优化
为了方便你快速了解其全貌,我将核心信息整理为下表:
| 特性维度 | 核心说明 |
|---|---|
| 本质与起源 | 轻量级 Java BPM 引擎,是 Activiti 5.x 的分支,支持 BPMN 2.0 标准 |
| 核心功能 | 流程设计、部署、实例执行与监控、任务管理、历史数据追踪、集成扩展 |
| 架构核心 (服务) | 通过 RepositoryService , RuntimeService , TaskService , HistoryService 等提供 API |
| 数据表 (ACT_开头) | 分为流程定义 (RE)、运行时 (RU)、历史 (HI)、身份 (ID)、通用 (GE) 五大类 |
| 主要应用场景 | 审批流程(请假、报销)、订单处理、工单流转等需要多人按规则协作的业务自动化 |
| 关键优势 | 轻量嵌入、与 Spring/Spring Boot 集成友好、功能全面且可扩展、拥有可视化设计器 |
# 核心概念与工作原理
理解 Flowable,需要掌握以下几个关键点:
流程定义与实例
流程定义:业务的蓝图,通常使用 BPMN 2.0 标准以 XML 格式定义,描述流程步骤、顺序和规则
流程实例:流程定义的一次具体运行。例如,一个请假流程定义,对应张三和李四各自发起的两次流程实例
流程引擎与服务
Flowable 的核心是一个流程引擎,它通过一系列服务接口提供所有功能:RepositoryService:管理(部署、查询、挂起)流程定义RuntimeService:启动和管理流程实例TaskService:处理用户任务(如查询待办、完成任务)HistoryService:查询已结束的流程实例历史数据任务与审批人分配
在用户任务节点,可以通过多种方式动态指定处理人
- 固定分配:在流程图中直接设置用户 ID。
- 表达式分配:使用 UEL 表达式,如
${departmentManager},在启动流程时传入变量。 - 监听器分配:通过编写 Java 代码,在任务创建时触发逻辑来指定。
- 流程变量
用于在流程实例运行过程中传递业务数据(如请假天数、审批意见),作用域可以是全局(整个实例)或局部(特定任务)
# 开始使用
首先本项目基于 flowable 专门封装了一个工作流的模块 scaffold-module-bpm 。
大概流程图:

定义流程模型 -> 发布成流程定义 -> 创建流程实例(就是一个启动的流程)-> 安装定义的流程节点顺序往下走 -> 审批、拒签、会签、委托 等等操作 -> 最终完成
就像水龙头中的水流一样,定义好整个供水系统的蓝图 -> 部署到需要的家家户户 -> 用户打开水龙头开始使用 -> 水从源头过来,途中会有多个闸口,有些检查余额的闸口,有钱放行,没钱不给水流通过(相当于审批) -> 最终出水使用
# 功能点
| 功能列表 | 功能描述 |
|---|---|
| SIMPLE 设计器 | 仿钉钉 / 飞书设计器,支持拖拽搭建表单流程,10 分钟快速完成审批流程配置 |
| BPMN 设计器 | 基于 BPMN 标准开发,适配复杂业务场景,满足多层级审批及流程自动化需求 |
| 会签 | 同一个审批节点设置多个人(如 A、B、C 三人,三人会同时收到待办任务),需全部同意之后,审批才可到下一审批节点 |
| 或签 | 同一个审批节点设置多个人,任意一个人处理后,就能进入下一个节点 |
| 依次审批 | (顺序会签)同一个审批节点设置多个人(如 A、B、C 三人),三人按顺序依次收到待办,即 A 先审批,A 提交后 B 才能审批,需全部同意之后,审批才可到下一审批节点 |
| 抄送 | 将审批结果通知给抄送人,同一个审批默认排重,不重复抄送给同一人 |
| 驳回 | (退回)将审批重置发送给某节点,重新审批。可驳回至发起人、上一节点、任意节点 |
| 转办 | A 转给其 B 审批,B 审批后,进入下一节点 |
| 委派 | A 转给其 B 审批,B 审批后,转给 A,A 继续审批后进入下一节点 |
| 加签 | 允许当前审批人根据需要,自行增加当前节点的审批人,支持向前、向后加签 |
| 减签 | (取消加签)在当前审批人操作之前,减少审批人 |
| 撤销 | (取消流程)流程发起人,可以对流程进行撤销处理 |
| 终止 | 系统管理员,在任意节点终止流程实例 |
| 表单权限 | 支持拖拉拽配置表单,每个审批节点可配置只读、编辑、隐藏权限 |
| 超时审批 | 配置超时审批时间,超时后自动触发审批通过、不通过、驳回等操作 |
| 自动提醒 | 配置提醒时间,到达时间后自动触发短信、邮箱、站内信等通知提醒,支持自定义重复提醒频次 |
| 父子流程 | 主流程设置子流程节点,子流程节点会自动触发子流程。子流程结束后,主流程才会执行(继续往下下执行),支持同步子流程、异步子流程 |
| 条件分支 | (排它分支)用于在流程中实现决策,即根据条件选择一个分支执行 |
| 并行分支 | 允许将流程分成多条分支,不进行条件判断,所有分支都会执行 |
| 包容分支 | (条件分支 + 并行分支的结合体)允许基于条件选择多条分支执行,但如果没有任何一个分支满足条件,则可以选择默认分支 |
| 路由分支 | 根据条件选择一个分支执行(重定向到指定配置节点),也可以选择默认分支执行(继续往下执行) |
| 触发节点 | 执行到该节点,触发 HTTP 请求、HTTP 回调、更新数据、删除数据等 |
| 延迟节点 | 执行到该节点,审批等待一段时间再执行,支持固定时长、固定日期等 |
| 拓展设置 | 流程前置 / 后置通知,节点(任务)前置、后置通知,流程报表,自动审批去重,自定流程编号、标题、摘要,流程报表等 |
# 具体实现
# 创建 scaffold-module-bpm 模块
<?xml version="1.0" encoding="UTF-8"?> | |
<project xmlns="http://maven.apache.org/POM/4.0.0" | |
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" | |
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> | |
<parent> | |
<artifactId>scaffold</artifactId> | |
<groupId>cn.tzzfj.scaffold</groupId> | |
<version>${revision}</version> | |
</parent> | |
<modelVersion>4.0.0</modelVersion> | |
<artifactId>scaffold-module-bpm</artifactId> | |
<packaging>jar</packaging> | |
<name>${project.artifactId}</name> | |
<description> | |
bpm 包下,业务流程管理(Business Process Management),我们放工作流的功能。 | |
例如说:流程定义、表单配置、审核中心(我的申请、我的待办、我的已办)等等 | |
bpm 解释:https://baike.baidu.com/item/BPM/1933 | |
工作流基于 Flowable 6 实现,分成流程定义、流程表单、流程实例、流程任务等功能模块。 | |
</description> | |
<dependencies> | |
<dependency> | |
<groupId>cn.tzzfj.scaffold</groupId> | |
<artifactId>scaffold-module-system</artifactId> | |
<version>${revision}</version> | |
</dependency> | |
<!-- 省略其他的依赖 --> | |
<!-- Flowable 工作流相关 --> | |
<dependency> | |
<groupId>org.flowable</groupId> | |
<artifactId>flowable-spring-boot-starter-process</artifactId> | |
</dependency> | |
<dependency> | |
<groupId>org.flowable</groupId> | |
<artifactId>flowable-spring-boot-starter-actuator</artifactId> | |
</dependency> | |
</dependencies> | |
</project> |
# 创建蓝图(流程模型)
# controller
package cn.tzzfj.scaffold.module.bpm.controller.admin.definition; | |
/** | |
* <p> Project: scaffold - BpmModelController </p> | |
* | |
* 管理后台 - 流程模型 | |
* | |
* @author Tz | |
* @date 2025/10/25 15:26 | |
* @version 1.0.0 | |
* @since 1.0.0 | |
*/ | |
@Tag(name = "管理后台 - 流程模型") | |
@RestController | |
@RequestMapping("/bpm/model") | |
@Validated | |
public class BpmModelController { | |
@Resource | |
private BpmModelService modelService; | |
@Resource | |
private BpmFormService formService; | |
@Resource | |
private BpmCategoryService categoryService; | |
@Resource | |
private BpmProcessDefinitionService processDefinitionService; | |
@Resource | |
private AdminUserApi adminUserApi; | |
@Resource | |
private DeptApi deptApi; | |
@GetMapping("/list") | |
@Operation(summary = "获得模型分页") | |
@Parameter(name = "name", description = "模型名称", example = "Tz") | |
public CommonResult<List<BpmModelRespVO>> getModelList(@RequestParam(value = "name", required = false) String name) { | |
List<Model> list = modelService.getModelList(name); | |
if (CollUtil.isEmpty(list)) { | |
return success(Collections.emptyList()); | |
} | |
// 获得 Form 表单 | |
Set<Long> formIds = convertSet(list, model -> { | |
BpmModelMetaInfoVO metaInfo = BpmModelConvert.INSTANCE.parseMetaInfo(model); | |
return metaInfo != null ? metaInfo.getFormId() : null; | |
}); | |
Map<Long, BpmFormDO> formMap = formService.getFormMap(formIds); | |
// 获得 Category Map | |
Map<String, BpmCategoryDO> categoryMap = categoryService.getCategoryMap( | |
convertSet(list, Model::getCategory)); | |
// 获得 Deployment Map | |
Map<String, Deployment> deploymentMap = processDefinitionService.getDeploymentMap( | |
convertSet(list, Model::getDeploymentId)); | |
// 获得 ProcessDefinition Map | |
List<ProcessDefinition> processDefinitions = processDefinitionService.getProcessDefinitionListByDeploymentIds( | |
deploymentMap.keySet()); | |
Map<String, ProcessDefinition> processDefinitionMap = convertMap(processDefinitions, ProcessDefinition::getDeploymentId); | |
// 获得 User Map、Dept Map | |
Set<Long> userIds = convertSetByFlatMap(list, model -> { | |
BpmModelMetaInfoVO metaInfo = BpmModelConvert.INSTANCE.parseMetaInfo(model); | |
return metaInfo != null ? metaInfo.getStartUserIds().stream() : Stream.empty(); | |
}); | |
Map<Long, AdminUserRespDTO> userMap = adminUserApi.getUserMap(userIds); | |
Set<Long> deptIds = convertSetByFlatMap(list, model -> { | |
BpmModelMetaInfoVO metaInfo = BpmModelConvert.INSTANCE.parseMetaInfo(model); | |
return metaInfo != null && metaInfo.getStartDeptIds() != null ? metaInfo.getStartDeptIds().stream() : Stream.empty(); | |
}); | |
Map<Long, DeptRespDTO> deptMap = deptApi.getDeptMap(deptIds); | |
// 最总组合信息返回 | |
return success(BpmModelConvert.INSTANCE.buildModelList(list, | |
formMap, categoryMap, deploymentMap, processDefinitionMap, userMap, deptMap)); | |
} | |
@GetMapping("/get") | |
@Operation(summary = "获得模型") | |
@Parameter(name = "id", description = "编号", required = true, example = "1024") | |
@PreAuthorize("@ss.hasPermission('bpm:model:query')") | |
public CommonResult<BpmModelRespVO> getModel(@RequestParam("id") String id) { | |
Model model = modelService.getModel(id); | |
if (model == null) { | |
return null; | |
} | |
byte[] bpmnBytes = modelService.getModelBpmnXML(id); | |
BpmSimpleModelNodeVO simpleModel = modelService.getSimpleModel(id); | |
return success(BpmModelConvert.INSTANCE.buildModel(model, bpmnBytes, simpleModel)); | |
} | |
@PostMapping("/create") | |
@Operation(summary = "新建模型") | |
@PreAuthorize("@ss.hasPermission('bpm:model:create')") | |
public CommonResult<String> createModel(@Valid @RequestBody BpmModelSaveReqVO createRetVO) { | |
return success(modelService.createModel(createRetVO)); | |
} | |
@PutMapping("/update") | |
@Operation(summary = "修改模型") | |
@PreAuthorize("@ss.hasPermission('bpm:model:update')") | |
public CommonResult<Boolean> updateModel(@Valid @RequestBody BpmModelSaveReqVO modelVO) { | |
modelService.updateModel(getLoginUserId(), modelVO); | |
return success(true); | |
} | |
@PutMapping("/update-sort-batch") | |
@Operation(summary = "批量修改模型排序") | |
@Parameter(name = "ids", description = "编号数组", required = true, example = "1,2,3") | |
public CommonResult<Boolean> updateModelSortBatch(@RequestParam("ids") List<String> ids) { | |
modelService.updateModelSortBatch(getLoginUserId(), ids); | |
return success(true); | |
} | |
@PostMapping("/deploy") | |
@Operation(summary = "部署模型") | |
@Parameter(name = "id", description = "编号", required = true, example = "1024") | |
@PreAuthorize("@ss.hasPermission('bpm:model:deploy')") | |
public CommonResult<Boolean> deployModel(@RequestParam("id") String id) { | |
modelService.deployModel(getLoginUserId(), id); | |
return success(true); | |
} | |
@PutMapping("/update-state") | |
@Operation(summary = "修改模型的状态", description = "实际更新的部署的流程定义的状态") | |
@PreAuthorize("@ss.hasPermission('bpm:model:update')") | |
public CommonResult<Boolean> updateModelState(@Valid @RequestBody BpmModelUpdateStateReqVO reqVO) { | |
modelService.updateModelState(getLoginUserId(), reqVO.getId(), reqVO.getState()); | |
return success(true); | |
} | |
@Deprecated | |
@PutMapping("/update-bpmn") | |
@Operation(summary = "修改模型的 BPMN") | |
@PreAuthorize("@ss.hasPermission('bpm:model:update')") | |
public CommonResult<Boolean> updateModelBpmn(@Valid @RequestBody BpmModeUpdateBpmnReqVO reqVO) { | |
modelService.updateModelBpmnXml(reqVO.getId(), reqVO.getBpmnXml()); | |
return success(true); | |
} | |
@DeleteMapping("/delete") | |
@Operation(summary = "删除模型") | |
@Parameter(name = "id", description = "编号", required = true, example = "1024") | |
@PreAuthorize("@ss.hasPermission('bpm:model:delete')") | |
public CommonResult<Boolean> deleteModel(@RequestParam("id") String id) { | |
modelService.deleteModel(getLoginUserId(), id); | |
return success(true); | |
} | |
@DeleteMapping("/clean") | |
@Operation(summary = "清理模型") | |
@Parameter(name = "id", description = "编号", required = true, example = "1024") | |
@PreAuthorize("@ss.hasPermission('bpm:model:clean')") | |
public CommonResult<Boolean> cleanModel(@RequestParam("id") String id) { | |
modelService.cleanModel(getLoginUserId(), id); | |
return success(true); | |
} | |
// ========== 仿钉钉 / 飞书的精简模型 ========= | |
@GetMapping("/simple/get") | |
@Operation(summary = "获得仿钉钉流程设计模型") | |
@Parameter(name = "modelId", description = "流程模型编号", required = true, example = "a2c5eee0-eb6c-11ee-abf4-0c37967c420a") | |
public CommonResult<BpmSimpleModelNodeVO> getSimpleModel(@RequestParam("id") String modelId){ | |
return success(modelService.getSimpleModel(modelId)); | |
} | |
@Deprecated | |
@PostMapping("/simple/update") | |
@Operation(summary = "保存仿钉钉流程设计模型") | |
@PreAuthorize("@ss.hasPermission('bpm:model:update')") | |
public CommonResult<Boolean> updateSimpleModel(@Valid @RequestBody BpmSimpleModelUpdateReqVO reqVO) { | |
modelService.updateSimpleModel(getLoginUserId(), reqVO); | |
return success(Boolean.TRUE); | |
} | |
} |
# service:
package cn.tzzfj.scaffold.module.bpm.service.definition; | |
/** | |
* <p> Project: scaffold - BpmModelServiceImpl </p> | |
* | |
* 流程模型实现:主要进行 Flowable {@link Model} 的维护 | |
* | |
* @author Tz | |
* @date 2025/10/25 15:26 | |
* @version 1.0.0 | |
* @since 1.0.0 | |
*/ | |
@Service | |
@Validated | |
@Slf4j | |
public class BpmModelServiceImpl implements BpmModelService { | |
@Resource | |
private RepositoryService repositoryService; | |
@Resource | |
private BpmProcessDefinitionService processDefinitionService; | |
@Resource | |
private BpmFormService bpmFormService; | |
@Resource | |
private BpmTaskCandidateInvoker taskCandidateInvoker; | |
@Resource | |
private HistoryService historyService; | |
@Resource | |
private RuntimeService runtimeService; | |
@Resource | |
private TaskService taskService; | |
@Resource | |
private BpmProcessInstanceCopyService processInstanceCopyService; | |
@Override | |
public List<Model> getModelList(String name) { | |
ModelQuery modelQuery = repositoryService.createModelQuery(); | |
if (StrUtil.isNotEmpty(name)) { | |
modelQuery.modelNameLike("%" + name + "%"); | |
} | |
modelQuery.modelTenantId(FlowableUtils.getTenantId()); | |
return modelQuery.list(); | |
} | |
@Override | |
public Long getModelCountByCategory(String category) { | |
return repositoryService.createModelQuery() | |
.modelCategory(category) | |
.modelTenantId(FlowableUtils.getTenantId()) | |
.count(); | |
} | |
@Override | |
@Transactional(rollbackFor = Exception.class) | |
public String createModel(@Valid BpmModelSaveReqVO createReqVO) { | |
if (!ValidationUtils.isXmlNCName(createReqVO.getKey())) { | |
throw exception(MODEL_KEY_VALID); | |
} | |
// 1. 校验流程标识已经存在 | |
Model keyModel = getModelByKey(createReqVO.getKey()); | |
if (keyModel != null) { | |
throw exception(MODEL_KEY_EXISTS, createReqVO.getKey()); | |
} | |
// 2. 创建 Model 对象 | |
createReqVO.setSort(System.currentTimeMillis()); // 使用当前时间,作为排序 | |
Model model = repositoryService.newModel(); | |
BpmModelConvert.INSTANCE.copyToModel(model, createReqVO); | |
model.setTenantId(FlowableUtils.getTenantId()); | |
// 3. 保存模型 | |
saveModel(model, createReqVO); | |
return model.getId(); | |
} | |
@Override | |
@Transactional(rollbackFor = Exception.class) // 因为进行多个操作,所以开启事务 | |
public void updateModel(Long userId, BpmModelSaveReqVO updateReqVO) { | |
// 1. 校验流程模型存在 | |
Model model = validateModelManager(updateReqVO.getId(), userId); | |
// 2. 填充 Model 信息 | |
BpmModelConvert.INSTANCE.copyToModel(model, updateReqVO); | |
// 3. 保存模型 | |
saveModel(model, updateReqVO); | |
} | |
/** | |
* 保存模型的基本信息、流程图 | |
* | |
* @param model 模型 | |
* @param saveReqVO 保存信息 | |
*/ | |
private void saveModel(Model model, BpmModelSaveReqVO saveReqVO) { | |
// 1. 保存模型的基础信息 | |
repositoryService.saveModel(model); | |
// 2. 保存流程图 | |
if (ObjUtil.equals(BpmModelTypeEnum.BPMN.getType(), saveReqVO.getType()) | |
&& StrUtil.isNotEmpty(saveReqVO.getBpmnXml())) { | |
updateModelBpmnXml(model.getId(), saveReqVO.getBpmnXml()); | |
} else if (ObjUtil.equals(BpmModelTypeEnum.SIMPLE.getType(), saveReqVO.getType()) | |
&& saveReqVO.getSimpleModel() != null) { | |
// JSON 转换成 bpmnModel | |
BpmnModel bpmnModel = SimpleModelUtils.buildBpmnModel(model.getKey(), model.getName(), | |
saveReqVO.getSimpleModel()); | |
// 保存 Bpmn XML | |
updateModelBpmnXml(model.getId(), BpmnModelUtils.getBpmnXml(bpmnModel)); | |
// 保存 JSON 数据 | |
updateModelSimpleJson(model.getId(), saveReqVO.getSimpleModel()); | |
} | |
} | |
@Override | |
@Transactional(rollbackFor = Exception.class) | |
public void updateModelSortBatch(Long userId, List<String> ids) { | |
// 1.1 校验流程模型存在 | |
List<Model> models = repositoryService.createModelQuery() | |
.modelTenantId(FlowableUtils.getTenantId()).list(); | |
models.removeIf(model -> !ids.contains(model.getId())); | |
if (ids.size() != models.size()) { | |
throw exception(MODEL_NOT_EXISTS); | |
} | |
Map<String, Model> modelMap = convertMap(models, Model::getId); | |
// 1.2 校验是否为管理员 | |
ids.forEach(id -> validateModelManager(id, userId)); | |
// 保存排序 | |
long sort = System.currentTimeMillis(); // 使用时间戳 - i 作为排序 | |
for (int i = ids.size() - 1; i > 0; i--) { | |
Model model = modelMap.get(ids.get(i)); | |
// 更新模型 | |
BpmModelMetaInfoVO metaInfo = BpmModelConvert.INSTANCE.parseMetaInfo(model).setSort(sort); | |
model.setMetaInfo(JsonUtils.toJsonString(metaInfo)); | |
repositoryService.saveModel(model); | |
// 更新排序 | |
processDefinitionService.updateProcessDefinitionSortByModelId(model.getId(), sort); | |
sort--; | |
} | |
} | |
private Model validateModelExists(String id) { | |
Model model = repositoryService.getModel(id); | |
if (model == null) { | |
throw exception(MODEL_NOT_EXISTS); | |
} | |
return model; | |
} | |
/** | |
* 校验是否有流程模型的管理权限 | |
* | |
* @param id 流程模型编号 | |
* @param userId 用户编号 | |
* @return 流程模型 | |
*/ | |
private Model validateModelManager(String id, Long userId) { | |
Model model = validateModelExists(id); | |
BpmModelMetaInfoVO metaInfo = BpmModelConvert.INSTANCE.parseMetaInfo(model); | |
if (metaInfo == null || !CollUtil.contains(metaInfo.getManagerUserIds(), userId)) { | |
throw exception(MODEL_UPDATE_FAIL_NOT_MANAGER, model.getName()); | |
} | |
return model; | |
} | |
@Override | |
@Transactional(rollbackFor = Exception.class) // 因为进行多个操作,所以开启事务 | |
public void deployModel(Long userId, String id) { | |
// 1.1 校验流程模型存在 | |
Model model = validateModelManager(id, userId); | |
BpmModelMetaInfoVO metaInfo = BpmModelConvert.INSTANCE.parseMetaInfo(model); | |
// 1.2 校验流程图 | |
byte[] bpmnBytes = getModelBpmnXML(model.getId()); | |
validateBpmnXml(bpmnBytes, metaInfo.getType()); | |
// 1.3 校验表单已配 | |
BpmFormDO form = validateFormConfig(metaInfo); | |
// 1.4 校验任务分配规则已配置 | |
taskCandidateInvoker.validateBpmnConfig(bpmnBytes); | |
// 1.5 获取仿钉钉流程设计器模型数据 | |
String simpleJson = getModelSimpleJson(model.getId()); | |
// 2.1 创建流程定义 | |
String definitionId = processDefinitionService.createProcessDefinition(model, metaInfo, bpmnBytes, simpleJson, | |
form); | |
// 2.2 将老的流程定义进行挂起。也就是说,只有最新部署的流程定义,才可以发起任务。 | |
updateProcessDefinitionSuspended(model.getDeploymentId()); | |
// 2.3 更新 model 的 deploymentId,进行关联 | |
ProcessDefinition definition = processDefinitionService.getProcessDefinition(definitionId); | |
model.setDeploymentId(definition.getDeploymentId()); | |
repositoryService.saveModel(model); | |
} | |
private void validateBpmnXml(byte[] bpmnBytes, Integer type) { | |
BpmnModel bpmnModel = BpmnModelUtils.getBpmnModel(bpmnBytes); | |
if (bpmnModel == null) { | |
throw exception(MODEL_NOT_EXISTS); | |
} | |
// 1. 没有 StartEvent | |
StartEvent startEvent = BpmnModelUtils.getStartEvent(bpmnModel); | |
if (startEvent == null) { | |
throw exception(MODEL_DEPLOY_FAIL_BPMN_START_EVENT_NOT_EXISTS); | |
} | |
// 2. 校验 UserTask 的 name 都配置了 | |
List<UserTask> userTasks = BpmnModelUtils.getBpmnModelElements(bpmnModel, UserTask.class); | |
userTasks.forEach(userTask -> { | |
if (StrUtil.isEmpty(userTask.getName())) { | |
throw exception(MODEL_DEPLOY_FAIL_BPMN_USER_TASK_NAME_NOT_EXISTS, userTask.getId()); | |
} | |
}); | |
// 3. 校验第一个用户任务节点的规则类型是否为 “审批人自选”,BPMN 设计器,校验第一个用户任务节点,SIMPLE 设计器,第一个节点固定为发起人所以校验第二个用户任务节点 | |
UserTask firUserTask = CollUtil.get(userTasks, BpmModelTypeEnum.BPMN.getType().equals(type) ? 0 : 1); | |
if (firUserTask == null) { | |
return; | |
} | |
Integer candidateStrategy = parseCandidateStrategy(firUserTask); | |
if (Objects.equals(candidateStrategy, BpmTaskCandidateStrategyEnum.APPROVE_USER_SELECT.getStrategy())) { | |
throw exception(MODEL_DEPLOY_FAIL_FIRST_USER_TASK_CANDIDATE_STRATEGY_ERROR, firUserTask.getName()); | |
} | |
} | |
@Override | |
@Transactional(rollbackFor = Exception.class) | |
public void deleteModel(Long userId, String id) { | |
// 校验流程模型存在 | |
Model model = validateModelManager(id, userId); | |
// 执行删除 | |
repositoryService.deleteModel(id); | |
// 禁用流程定义 | |
updateProcessDefinitionSuspended(model.getDeploymentId()); | |
} | |
@Override | |
public void cleanModel(Long userId, String id) { | |
// 1. 校验流程模型存在 | |
Model model = validateModelManager(id, userId); | |
// 2. 清理所有流程数据 | |
// 2.1 先取消所有正在运行的流程 | |
List<ProcessInstance> processInstances = runtimeService.createProcessInstanceQuery() | |
.processDefinitionKey(model.getKey()).list(); | |
processInstances.forEach(processInstance -> { | |
runtimeService.deleteProcessInstance(processInstance.getId(), | |
BpmReasonEnum.CANCEL_BY_SYSTEM.getReason()); | |
historyService.deleteHistoricProcessInstance(processInstance.getId()); | |
processInstanceCopyService.deleteProcessInstanceCopy(processInstance.getId()); | |
}); | |
// 2.2 再从历史中删除所有相关的流程数据 | |
List<HistoricProcessInstance> historicProcessInstances = historyService.createHistoricProcessInstanceQuery() | |
.processDefinitionKey(model.getKey()).list(); | |
historicProcessInstances.forEach(historicProcessInstance -> { | |
historyService.deleteHistoricProcessInstance(historicProcessInstance.getId()); | |
processInstanceCopyService.deleteProcessInstanceCopy(historicProcessInstance.getId()); | |
}); | |
// 2.3 清理所有 Task | |
List<Task> tasks = taskService.createTaskQuery() | |
.processDefinitionKey(model.getKey()).list(); | |
tasks.forEach(task -> taskService.deleteTask(task.getId(),BpmReasonEnum.CANCEL_BY_PROCESS_CLEAN.getReason())); | |
} | |
@Override | |
public void updateModelState(Long userId, String id, Integer state) { | |
// 1.1 校验流程模型存在 | |
Model model = validateModelManager(id, userId); | |
// 1.2 校验流程定义存在 | |
ProcessDefinition definition = processDefinitionService | |
.getProcessDefinitionByDeploymentId(model.getDeploymentId()); | |
if (definition == null) { | |
throw exception(PROCESS_DEFINITION_NOT_EXISTS); | |
} | |
// 2. 更新状态 | |
processDefinitionService.updateProcessDefinitionState(definition.getId(), state); | |
} | |
@Override | |
public BpmnModel getBpmnModelByDefinitionId(String processDefinitionId) { | |
return repositoryService.getBpmnModel(processDefinitionId); | |
} | |
@Override | |
public BpmSimpleModelNodeVO getSimpleModel(String modelId) { | |
Model model = validateModelExists(modelId); | |
// 通过 ACT_RE_MODEL 表 EDITOR_SOURCE_EXTRA_VALUE_ID_ ,获取仿钉钉快搭模型的 JSON 数据 | |
String json = getModelSimpleJson(model.getId()); | |
return JsonUtils.parseObject(json, BpmSimpleModelNodeVO.class); | |
} | |
@Override | |
public void updateSimpleModel(Long userId, BpmSimpleModelUpdateReqVO reqVO) { | |
// 1. 校验流程模型存在 | |
Model model = validateModelManager(reqVO.getId(), userId); | |
// 2.1 JSON 转换成 bpmnModel | |
BpmnModel bpmnModel = SimpleModelUtils.buildBpmnModel(model.getKey(), model.getName(), reqVO.getSimpleModel()); | |
// 2.2 保存 Bpmn XML | |
updateModelBpmnXml(model.getId(), BpmnModelUtils.getBpmnXml(bpmnModel)); | |
// 2.3 保存 JSON 数据 | |
updateModelSimpleJson(model.getId(), reqVO.getSimpleModel()); | |
} | |
/** | |
* 校验流程表单已配置 | |
* | |
* @param metaInfo 流程模型元数据 | |
* @return 表单配置 | |
*/ | |
private BpmFormDO validateFormConfig(BpmModelMetaInfoVO metaInfo) { | |
if (metaInfo == null || metaInfo.getFormType() == null) { | |
throw exception(MODEL_DEPLOY_FAIL_FORM_NOT_CONFIG); | |
} | |
// 校验表单存在 | |
if (Objects.equals(metaInfo.getFormType(), BpmModelFormTypeEnum.NORMAL.getType())) { | |
if (metaInfo.getFormId() == null) { | |
throw exception(MODEL_DEPLOY_FAIL_FORM_NOT_CONFIG); | |
} | |
BpmFormDO form = bpmFormService.getForm(metaInfo.getFormId()); | |
if (form == null) { | |
throw exception(FORM_NOT_EXISTS); | |
} | |
return form; | |
} else { | |
if (StrUtil.isEmpty(metaInfo.getFormCustomCreatePath()) | |
|| StrUtil.isEmpty(metaInfo.getFormCustomViewPath())) { | |
throw exception(MODEL_DEPLOY_FAIL_FORM_NOT_CONFIG); | |
} | |
return null; | |
} | |
} | |
@Override | |
public void updateModelBpmnXml(String id, String bpmnXml) { | |
if (StrUtil.isEmpty(bpmnXml)) { | |
return; | |
} | |
repositoryService.addModelEditorSource(id, StrUtil.utf8Bytes(bpmnXml)); | |
} | |
@SuppressWarnings("JavaExistingMethodCanBeUsed") | |
private String getModelSimpleJson(String id) { | |
byte[] bytes = repositoryService.getModelEditorSourceExtra(id); | |
if (ArrayUtil.isEmpty(bytes)) { | |
return null; | |
} | |
return StrUtil.utf8Str(bytes); | |
} | |
private void updateModelSimpleJson(String id, BpmSimpleModelNodeVO node) { | |
if (node == null) { | |
return; | |
} | |
byte[] bytes = JsonUtils.toJsonByte(node); | |
repositoryService.addModelEditorSourceExtra(id, bytes); | |
} | |
/** | |
* 挂起 deploymentId 对应的流程定义 | |
* <p> | |
* 注意:这里一个 deploymentId 只关联一个流程定义 | |
* | |
* @param deploymentId 流程发布 Id | |
*/ | |
private void updateProcessDefinitionSuspended(String deploymentId) { | |
if (StrUtil.isEmpty(deploymentId)) { | |
return; | |
} | |
ProcessDefinition oldDefinition = processDefinitionService.getProcessDefinitionByDeploymentId(deploymentId); | |
if (oldDefinition == null) { | |
return; | |
} | |
processDefinitionService.updateProcessDefinitionState(oldDefinition.getId(), | |
SuspensionState.SUSPENDED.getStateCode()); | |
} | |
private Model getModelByKey(String key) { | |
return repositoryService.createModelQuery() | |
.modelTenantId(FlowableUtils.getTenantId()) | |
.modelKey(key).singleResult(); | |
} | |
@Override | |
public Model getModel(String id) { | |
return repositoryService.getModel(id); | |
} | |
@Override | |
public byte[] getModelBpmnXML(String id) { | |
return repositoryService.getModelEditorSource(id); | |
} | |
} |
# 解释:
创建流程模型,
repositoryService.saveModel(model)添加
表单、BPM XML 流程图、其他相关配置对应的表
act_re_model并且在字段meteinfo保存了额为的扩展数据package cn.tzzfj.scaffold.module.bpm.controller.admin.definition.vo.model;
/*** <p> Project: scaffold - BpmModelMetaInfoVO </p>
*
* 流程图标
*
* @author Tz
* @date 2025/10/25 15:26
* @version 1.0.0
* @since 1.0.0
*/
@Datapublic class BpmModelMetaInfoVO {
@Schema(description = "流程图标", example = "https://www.tzzfj.cn/scaffold.jpg")
@URL(message = "流程图标格式不正确")
private String icon;
@Schema(description = "流程描述", example = "我是描述")
private String description;
@Schema(description = "流程类型", requiredMode = Schema.RequiredMode.REQUIRED, example = "10")
@InEnum(BpmModelTypeEnum.class)
@NotNull(message = "流程类型不能为空")
private Integer type;
@Schema(description = "表单类型", requiredMode = Schema.RequiredMode.REQUIRED, example = "10")
@InEnum(BpmModelFormTypeEnum.class)
@NotNull(message = "表单类型不能为空")
private Integer formType;
@Schema(description = "表单编号", example = "1024")
private Long formId; //formType 为 NORMAL 使用,必须非空
@Schema(description = "自定义表单的提交路径,使用 Vue 的路由地址", example = "/bpm/oa/leave/create")
private String formCustomCreatePath; // 表单类型为 CUSTOM 时,必须非空
@Schema(description = "自定义表单的查看路径,使用 Vue 的路由地址", example = "/bpm/oa/leave/view")
private String formCustomViewPath; // 表单类型为 CUSTOM 时,必须非空
@Schema(description = "是否可见", requiredMode = Schema.RequiredMode.REQUIRED, example = "true")
@NotNull(message = "是否可见不能为空")
private Boolean visible;
@Schema(description = "可发起用户编号数组", example = "[1,2,3]")
private List<Long> startUserIds;
@Schema(description = "可发起部门编号数组", example = "[2,4,6]")
private List<Long> startDeptIds;
@Schema(description = "可管理用户编号数组", requiredMode = Schema.RequiredMode.REQUIRED, example = "[2,4,6]")
@NotEmpty(message = "可管理用户编号数组不能为空")
private List<Long> managerUserIds;
@Schema(description = "排序", example = "1")
private Long sort; // 创建时,后端自动生成
@Schema(description = "允许撤销审批中的申请", example = "true")
private Boolean allowCancelRunningProcess;
@Schema(description = "允许允许审批人撤回任务", example = "false")
private Boolean allowWithdrawTask;
@Schema(description = "流程 ID 规则", example = "{}")
private ProcessIdRule processIdRule;
@Schema(description = "自动去重类型", example = "1")
@InEnum(BpmAutoApproveTypeEnum.class)
private Integer autoApprovalType;
@Schema(description = "标题设置", example = "{}")
private TitleSetting titleSetting;
@Schema(description = "摘要设置", example = "{}")
private SummarySetting summarySetting;
@Schema(description = "流程前置通知设置", example = "{}")
private HttpRequestSetting processBeforeTriggerSetting;
@Schema(description = "流程后置通知设置", example = "{}")
private HttpRequestSetting processAfterTriggerSetting;
@Schema(description = "任务前置通知设置", example = "{}")
private HttpRequestSetting taskBeforeTriggerSetting;
@Schema(description = "任务后置通知设置", example = "{}")
private HttpRequestSetting taskAfterTriggerSetting;
@Schema(description = "自定义打印模板设置", example = "{}")
@Validprivate PrintTemplateSetting printTemplateSetting;
@Schema(description = "流程 ID 规则")
@Data@Validpublic static class ProcessIdRule {
@Schema(description = "是否启用", example = "false")
@NotNull(message = "是否启用不能为空")
private Boolean enable;
@Schema(description = "前缀", example = "XX")
private String prefix;
@Schema(description = "中缀", example = "20250120")
private String infix; // 精确到日、精确到时、精确到分、精确到秒
@Schema(description = "后缀", example = "YY")
private String postfix;
@Schema(description = "序列长度", example = "5")
@NotNull(message = "序列长度不能为空")
private Integer length;
}@Schema(description = "标题设置")
@Data@Validpublic static class TitleSetting {
@Schema(description = "是否自定义", example = "false")
@NotNull(message = "是否自定义不能为空")
private Boolean enable;
@Schema(description = "标题", example = "流程标题")
private String title;
}@Schema(description = "摘要设置")
@Data@Validpublic static class SummarySetting {
@Schema(description = "是否自定义", example = "false")
@NotNull(message = "是否自定义不能为空")
private Boolean enable;
@Schema(description = "摘要字段数组", example = "[]")
private List<String> summary;
}@Schema(description = "http 请求通知设置", example = "{}")
@Datapublic static class HttpRequestSetting {
@Schema(description = "请求路径", example = "http://127.0.0.1")
@NotEmpty(message = "请求 URL 不能为空")
@URL(message = "请求 URL 格式不正确")
private String url;
@Schema(description = "请求头参数设置", example = "[]")
@Validprivate List<BpmSimpleModelNodeVO.HttpRequestParam> header;
@Schema(description = "请求头参数设置", example = "[]")
@Validprivate List<BpmSimpleModelNodeVO.HttpRequestParam> body;
/*** 请求返回处理设置,用于修改流程表单值
* <p>
* key:表示要修改的流程表单字段名 (name)
* value:接口返回的字段名
*/
@Schema(description = "请求返回处理设置", example = "[]")
private List<KeyValue<String, String>> response;
}@Schema(description = "自定义打印模板设置")
@Datapublic static class PrintTemplateSetting {
@Schema(description = "是否自定义打印模板", example = "false")
@NotNull(message = "是否自定义打印模板不能为空")
private Boolean enable;
@Schema(description = "打印模板", example = "<p></p>")
private String template;
}}
# 部署流程(流程定义)
# controller
/** | |
* <p> Project: scaffold - BpmProcessDefinitionController </p> | |
* | |
* 管理后台 - 流程定义 | |
* | |
* @author Tz | |
* @date 2025/10/25 15:26 | |
* @version 1.0.0 | |
* @since 1.0.0 | |
*/ | |
@Tag(name = "管理后台 - 流程定义") | |
@RestController | |
@RequestMapping("/bpm/process-definition") | |
@Validated | |
public class BpmProcessDefinitionController { | |
@Resource | |
private BpmProcessDefinitionService processDefinitionService; | |
@Resource | |
private BpmFormService formService; | |
@Resource | |
private BpmCategoryService categoryService; | |
@GetMapping("/page") | |
@Operation(summary = "获得流程定义分页") | |
@PreAuthorize("@ss.hasPermission('bpm:process-definition:query')") | |
public CommonResult<PageResult<BpmProcessDefinitionRespVO>> getProcessDefinitionPage( | |
BpmProcessDefinitionPageReqVO pageReqVO) { | |
PageResult<ProcessDefinition> pageResult = processDefinitionService.getProcessDefinitionPage(pageReqVO); | |
if (CollUtil.isEmpty(pageResult.getList())) { | |
return success(PageResult.empty(pageResult.getTotal())); | |
} | |
// 获得 Category Map | |
Map<String, BpmCategoryDO> categoryMap = categoryService.getCategoryMap( | |
convertSet(pageResult.getList(), ProcessDefinition::getCategory)); | |
// 获得 Deployment Map | |
Map<String, Deployment> deploymentMap = processDefinitionService.getDeploymentMap( | |
convertSet(pageResult.getList(), ProcessDefinition::getDeploymentId)); | |
// 获得 BpmProcessDefinitionInfoDO Map | |
Map<String, BpmProcessDefinitionInfoDO> processDefinitionMap = processDefinitionService.getProcessDefinitionInfoMap( | |
convertSet(pageResult.getList(), ProcessDefinition::getId)); | |
// 获得 Form Map | |
Map<Long, BpmFormDO> formMap = formService.getFormMap( | |
convertSet(processDefinitionMap.values(), BpmProcessDefinitionInfoDO::getFormId)); | |
return success(BpmProcessDefinitionConvert.INSTANCE.buildProcessDefinitionPage( | |
pageResult, deploymentMap, processDefinitionMap, formMap, categoryMap)); | |
} | |
@GetMapping ("/list") | |
@Operation(summary = "获得流程定义列表") | |
@Parameter(name = "suspensionState", description = "挂起状态", required = true, example = "1") // 参见 Flowable SuspensionState 枚举 | |
public CommonResult<List<BpmProcessDefinitionRespVO>> getProcessDefinitionList( | |
@RequestParam("suspensionState") Integer suspensionState) { | |
// 1.1 获得开启的流程定义 | |
List<ProcessDefinition> list = processDefinitionService.getProcessDefinitionListBySuspensionState(suspensionState); | |
if (CollUtil.isEmpty(list)) { | |
return success(Collections.emptyList()); | |
} | |
// 1.2 移除不可见的流程定义 | |
Map<String, BpmProcessDefinitionInfoDO> processDefinitionMap = processDefinitionService.getProcessDefinitionInfoMap( | |
convertSet(list, ProcessDefinition::getId)); | |
Long userId = getLoginUserId(); | |
list.removeIf(processDefinition -> { | |
BpmProcessDefinitionInfoDO processDefinitionInfo = processDefinitionMap.get(processDefinition.getId()); | |
return processDefinitionInfo == null // 不存在 | |
|| Boolean.FALSE.equals(processDefinitionInfo.getVisible()) //visible 不可见 | |
|| !processDefinitionService.canUserStartProcessDefinition(processDefinitionInfo, userId); // 无权限发起 | |
}); | |
// 2. 拼接 VO 返回 | |
return success(BpmProcessDefinitionConvert.INSTANCE.buildProcessDefinitionList( | |
list, null, processDefinitionMap, null, null)); | |
} | |
@GetMapping("/simple-list") | |
@Operation(summary = "获得流程定义精简列表", description = "只包含未挂起的流程,主要用于前端的下拉选项") | |
public CommonResult<List<BpmProcessDefinitionRespVO>> getSimpleProcessDefinitionList() { | |
// 只查询未挂起的流程 | |
List<ProcessDefinition> list = processDefinitionService.getProcessDefinitionListBySuspensionState( | |
SuspensionState.ACTIVE.getStateCode()); | |
// 拼接 VO 返回,只返回 id、name、key | |
return success(convertList(list, definition -> new BpmProcessDefinitionRespVO() | |
.setId(definition.getId()).setName(definition.getName()).setKey(definition.getKey()))); | |
} | |
@GetMapping ("/get") | |
@Operation(summary = "获得流程定义") | |
@Parameter(name = "id", description = "流程编号", required = true, example = "1024") | |
@Parameter(name = "key", description = "流程定义标识", required = true, example = "1024") | |
public CommonResult<BpmProcessDefinitionRespVO> getProcessDefinition( | |
@RequestParam(value = "id", required = false) String id, | |
@RequestParam(value = "key", required = false) String key) { | |
ProcessDefinition processDefinition = id != null ? processDefinitionService.getProcessDefinition(id) | |
: processDefinitionService.getActiveProcessDefinition(key); | |
if (processDefinition == null) { | |
return success(null); | |
} | |
BpmProcessDefinitionInfoDO processDefinitionInfo = processDefinitionService.getProcessDefinitionInfo(processDefinition.getId()); | |
BpmnModel bpmnModel = processDefinitionService.getProcessDefinitionBpmnModel(processDefinition.getId()); | |
return success(BpmProcessDefinitionConvert.INSTANCE.buildProcessDefinition( | |
processDefinition, null, processDefinitionInfo, null, null, bpmnModel)); | |
} | |
} |
# service:
package cn.tzzfj.scaffold.module.bpm.service.definition; | |
/** | |
* <p> Project: scaffold - BpmProcessDefinitionServiceImpl </p> | |
* | |
* 流程定义实现 | |
* | |
* 主要进行 Flowable {@link ProcessDefinition} 和 {@link Deployment} 的维护 | |
* | |
* @author Tz | |
* @date 2025/10/25 15:26 | |
* @version 1.0.0 | |
* @since 1.0.0 | |
*/ | |
@Service | |
@Validated | |
@Slf4j | |
public class BpmProcessDefinitionServiceImpl implements BpmProcessDefinitionService { | |
@Resource | |
private RepositoryService repositoryService; | |
@Resource | |
private BpmProcessDefinitionInfoMapper processDefinitionMapper; | |
@Resource | |
private AdminUserApi adminUserApi; | |
@Override | |
public ProcessDefinition getProcessDefinition(String id) { | |
return repositoryService.getProcessDefinition(id); | |
} | |
@Override | |
public List<ProcessDefinition> getProcessDefinitionList(Set<String> ids) { | |
return repositoryService.createProcessDefinitionQuery().processDefinitionIds(ids).list(); | |
} | |
@Override | |
public ProcessDefinition getProcessDefinitionByDeploymentId(String deploymentId) { | |
if (StrUtil.isEmpty(deploymentId)) { | |
return null; | |
} | |
return repositoryService.createProcessDefinitionQuery().deploymentId(deploymentId).singleResult(); | |
} | |
@Override | |
public List<ProcessDefinition> getProcessDefinitionListByDeploymentIds(Set<String> deploymentIds) { | |
if (CollUtil.isEmpty(deploymentIds)) { | |
return emptyList(); | |
} | |
return repositoryService.createProcessDefinitionQuery().deploymentIds(deploymentIds).list(); | |
} | |
@Override | |
public ProcessDefinition getActiveProcessDefinition(String key) { | |
return repositoryService.createProcessDefinitionQuery() | |
.processDefinitionTenantId(FlowableUtils.getTenantId()) | |
.processDefinitionKey(key).active().singleResult(); | |
} | |
@Override | |
public boolean canUserStartProcessDefinition(BpmProcessDefinitionInfoDO processDefinition, Long userId) { | |
if (processDefinition == null) { | |
return false; | |
} | |
// 校验用户是否在允许发起的用户列表中 | |
if (CollUtil.isNotEmpty(processDefinition.getStartUserIds())) { | |
return processDefinition.getStartUserIds().contains(userId); | |
} | |
// 校验用户是否在允许发起的部门列表中 | |
if (CollUtil.isNotEmpty(processDefinition.getStartDeptIds())) { | |
AdminUserRespDTO user = adminUserApi.getUser(userId); | |
return user != null | |
&& user.getDeptId() != null | |
&& processDefinition.getStartDeptIds().contains(user.getDeptId()); | |
} | |
// 都为空,则所有人都可以发起 | |
return true; | |
} | |
@Override | |
public List<Deployment> getDeploymentList(Set<String> ids) { | |
if (CollUtil.isEmpty(ids)) { | |
return emptyList(); | |
} | |
List<Deployment> list = new ArrayList<>(ids.size()); | |
for (String id : ids) { | |
addIfNotNull(list, getDeployment(id)); | |
} | |
return list; | |
} | |
@Override | |
public Deployment getDeployment(String id) { | |
if (StrUtil.isEmpty(id)) { | |
return null; | |
} | |
return repositoryService.createDeploymentQuery().deploymentId(id).singleResult(); | |
} | |
@Override | |
public String createProcessDefinition(Model model, BpmModelMetaInfoVO modelMetaInfo, | |
byte[] bpmnBytes, String simpleJson, BpmFormDO form) { | |
// 创建 Deployment 部署 | |
Deployment deploy = repositoryService.createDeployment() | |
.key(model.getKey()).name(model.getName()).category(model.getCategory()) | |
.addBytes(model.getKey() + BpmnModelConstants.BPMN_FILE_SUFFIX, bpmnBytes) | |
.tenantId(FlowableUtils.getTenantId()) | |
.disableSchemaValidation() // 禁用 XML Schema 验证,因为有自定义的属性 | |
.deploy(); | |
// 设置 ProcessDefinition 的 category 分类 | |
ProcessDefinition definition = repositoryService.createProcessDefinitionQuery() | |
.deploymentId(deploy.getId()).singleResult(); | |
repositoryService.setProcessDefinitionCategory(definition.getId(), model.getCategory()); | |
// 注意 1,ProcessDefinition 的 key 和 name 是通过 BPMN 中的 <bpmn2:process /> 的 id 和 name 决定 | |
// 注意 2,目前该项目的设计上,需要保证 Model、Deployment、ProcessDefinition 使用相同的 key,保证关联性。 | |
// 否则,会导致 ProcessDefinition 的分页无法查询到。 | |
if (!Objects.equals(definition.getKey(), model.getKey())) { | |
throw exception(PROCESS_DEFINITION_KEY_NOT_MATCH, model.getKey(), definition.getKey()); | |
} | |
if (!Objects.equals(definition.getName(), model.getName())) { | |
throw exception(PROCESS_DEFINITION_NAME_NOT_MATCH, model.getName(), definition.getName()); | |
} | |
// 插入拓展表 | |
BpmProcessDefinitionInfoDO definitionDO = BeanUtils.toBean(modelMetaInfo, BpmProcessDefinitionInfoDO.class) | |
.setModelId(model.getId()).setCategory(model.getCategory()).setProcessDefinitionId(definition.getId()) | |
.setModelType(modelMetaInfo.getType()).setSimpleModel(simpleJson); | |
if (form != null) { | |
definitionDO.setFormFields(form.getFields()).setFormConf(form.getConf()); | |
} | |
processDefinitionMapper.insert(definitionDO); | |
return definition.getId(); | |
} | |
@Override | |
public void updateProcessDefinitionState(String id, Integer state) { | |
ProcessDefinition processDefinition = repositoryService.getProcessDefinition(id); | |
if (processDefinition == null) { | |
throw exception(PROCESS_DEFINITION_NOT_EXISTS); | |
} | |
// 激活 | |
if (Objects.equals(SuspensionState.ACTIVE.getStateCode(), state)) { | |
if (processDefinition.isSuspended()) { | |
repositoryService.activateProcessDefinitionById(id, false, null); | |
} | |
return; | |
} | |
// 挂起 | |
if (Objects.equals(SuspensionState.SUSPENDED.getStateCode(), state)) { | |
//suspendProcessInstances = false,进行中的任务,不进行挂起。 | |
// 原因:只要新的流程不允许发起即可,老流程继续可以执行。 | |
if (!processDefinition.isSuspended()) { | |
repositoryService.suspendProcessDefinitionById(id, false, null); | |
} | |
return; | |
} | |
log.error("[updateProcessDefinitionState][流程定义({}) 修改未知状态({})]", id, state); | |
} | |
@Override | |
public void updateProcessDefinitionSortByModelId(String modelId, Long sort) { | |
processDefinitionMapper.updateByModelId(modelId, new BpmProcessDefinitionInfoDO().setSort(sort)); | |
} | |
@Override | |
public BpmnModel getProcessDefinitionBpmnModel(String id) { | |
return repositoryService.getBpmnModel(id); | |
} | |
@Override | |
public BpmProcessDefinitionInfoDO getProcessDefinitionInfo(String id) { | |
return processDefinitionMapper.selectByProcessDefinitionId(id); | |
} | |
@Override | |
public List<BpmProcessDefinitionInfoDO> getProcessDefinitionInfoList(Collection<String> ids) { | |
return processDefinitionMapper.selectListByProcessDefinitionIds(ids); | |
} | |
@Override | |
public PageResult<ProcessDefinition> getProcessDefinitionPage(BpmProcessDefinitionPageReqVO pageVO) { | |
ProcessDefinitionQuery query = repositoryService.createProcessDefinitionQuery(); | |
query.processDefinitionTenantId(FlowableUtils.getTenantId()); | |
if (StrUtil.isNotBlank(pageVO.getKey())) { | |
query.processDefinitionKey(pageVO.getKey()); | |
} | |
// 执行查询 | |
long count = query.count(); | |
if (count == 0) { | |
return PageResult.empty(count); | |
} | |
List<ProcessDefinition> list = query.orderByProcessDefinitionVersion().desc() | |
.listPage(PageUtils.getStart(pageVO), pageVO.getPageSize()); | |
return new PageResult<>(list, count); | |
} | |
@Override | |
public List<ProcessDefinition> getProcessDefinitionListBySuspensionState(Integer suspensionState) { | |
// 拼接查询条件 | |
ProcessDefinitionQuery query = repositoryService.createProcessDefinitionQuery(); | |
if (Objects.equals(SuspensionState.SUSPENDED.getStateCode(), suspensionState)) { | |
query.suspended(); | |
} else if (Objects.equals(SuspensionState.ACTIVE.getStateCode(), suspensionState)) { | |
query.active(); | |
} | |
// 执行查询 | |
query.processDefinitionTenantId(FlowableUtils.getTenantId()); | |
return query.list(); | |
} | |
} |
# 解释:
创建流程模型,
repositoryService.saveModel(model)添加
表单、BPM XML 流程图、其他相关配置对应的表
act_re_model并且在字段meteinfo保存了额为的扩展数据