这里使用的是SpringBoot3.2+Flowable6.8.0。过程中没有使用到Flowable UI
流程分类
分类功能只是简单的增删改查,就不多展示代码了。
新增

/**
* 新增流程分类
*/
@Operation(description = "新增流程分类")
@PostMapping("save")
public R<Void> add(@Validated(value = AddGroup.class) @RequestBody WfCategoryDTO category) {
return toAjax(categoryService.insert(category));
}
删除
/**
* 删除流程分类
*/
@Operation(description = "删除流程分类")
@PutMapping("delete/{categoryIds}")
public R<Void> delete(@NotEmpty(message = "主键不能为空") @PathVariable Long[] categoryIds) {
return toAjax(categoryService.deleteByIds(categoryIds));
}
修改
/**
* 修改流程分类
*/
@Operation(description = "修改流程分类")
@PutMapping("edit")
public R<Void> edit(@Validated(value = EditGroup.class) @RequestBody WfCategoryDTO category) {
return toAjax(categoryService.update(category));
}
查询
/**
* 查询流程分类列表(含分页)
*
* @param category 查询条件
* @param pageQuery 分页参数
* @return
*/
@GetMapping("list")
@Operation(description = "查询流程分类列表")
public TableDataInfo<WfCategoryVo> list(WfCategoryDTO category, PageQuery pageQuery) {
return categoryService.queryPageList(category, pageQuery);
}
表单配置
表单使用的开源的vform
新增

/**
* 新增流程表单
*
* @param wfFormDTO 表单对象
*/
@PostMapping("save")
@Operation(description = "新增流程表单")
public R<Void> add(@Validated(value = AddGroup.class) @RequestBody WfFormDTO wfFormDTO) {
return toAjax(wfFormService.insert(wfFormDTO));
}
删除
/**
* 删除流程表单
*
* @param formIds 主键串
*/
@Operation(description = "删除流程表单")
@DeleteMapping("/{formIds}")
public R<Void> remove(@NotEmpty(message = "主键不能为空") @PathVariable Long[] formIds) {
return toAjax(wfFormService.deleteByIds(Arrays.asList(formIds)) > 0 ? 1 : 0);
}
修改
/**
* 修改流程表单
*
* @param wfFormDTO 表单对象
*/
@Operation(description = "编辑流程表单")
@PutMapping("edit")
public R<Void> edit(@Validated(value = EditGroup.class) @RequestBody WfFormDTO wfFormDTO) {
return toAjax(wfFormService.update(wfFormDTO));
}
查询
/**
* 查询流程表单列表
*/
@Operation(description = "查询流程表单列表")
@GetMapping("/list")
public TableDataInfo<WfFormVo> list(@Validated(QueryGroup.class) WfFormDTO wfFormDTO, PageQuery pageQuery) {
return wfFormService.queryPageList(wfFormDTO, pageQuery);
}
流程模型
这一部分就涉及到了flowable中的内容了,这里需要先介绍一些概念。
Process Engine(流程引擎)
Process Engine 是 Flowable 的核心,负责管理流程实例的生命周期和所有与流程相关的操作。它为开发者提供了一系列的 API 接口,能够调用相关的服务来进行流程定义、流程实例的启动、任务管理、表单处理等功能。
Process Engine 可以被理解为整个系统的 "中枢",通过它可以访问其他的服务组件。典型的服务组件包括:
- RepositoryService:用于管理流程定义的部署、查询、挂起、删除等操作。
- RuntimeService:处理流程实例的启动、查询、暂停、恢复和变量管理等操作。
- TaskService:用于管理用户任务,任务的查询、认领、完成等。
- IdentityService:负责管理与流程相关的用户和组(如权限、身份验证)。
- HistoryService:提供对流程实例和任务的历史记录查询和管理。
- ManagementService:用于管理和监控流程引擎的系统功能,如作业的查询和执行。
新增
这里我是将新增和流程图设计分开来,首先先新增一个流程模型(包括流程标识、流程名称、流程分类等),然后再此基础上进行流程图的设计。

@Transactional
@Override
public void insert(WfModelDTO wfModelDTO) {
// 创建一个模型,Model是由flowable提供的
Model model = repositoryService.newModel();
model.setCategory(wfModelDTO.getCategory());
model.setKey(wfModelDTO.getKey());
model.setName(wfModelDTO.getName());
model.setVersion(1);
// 创建一个ModelMetaInfo对象,MetaInfo 是 Flowable 中的 Model 对象的一个属性,通常用来存储模型的元数据信息。这些元数据通常以 JSON 格式存储,用来描述模型的额外信息,包括模型的描述信息、版本号、作者或创建者的名字、创建时间或最后修改时间、其他自定义的元数据信息
ModelMetaInfoBO modelMetaInfoBO = new ModelMetaInfoBO();
modelMetaInfoBO.setCreateUser("admin");
modelMetaInfoBO.setCreateTime(new Date());
modelMetaInfoBO.setVersion(1);
modelMetaInfoBO.setDesc(wfModelDTO.getDesc());
model.setMetaInfo(JSON.toJSONString(modelMetaInfoBO));
// 通过RepositoryService保存
repositoryService.saveModel(model);
}
设计

这个方法就是对新增的流程模型进行流程图设计,流程图是以xml格式进行保存的。
/**
* 这里要这样理解,设计流程图不能是在原来的基础上修改,而是新增,对版本号+1,然后保存,这里可以看实际需求,有些业务认为设计流程图也是算对流程的一次修改,所 * 以这里看自己业务需求决定
*
* @param wfModelDTO
*/
@Override
@Transactional
public void saveBpmnXml(WfBpmnModelDTO wfModelDTO) {
//查询模型信息
String modelId = wfModelDTO.getModeId();
Model model = repositoryService.getModel(modelId);
if (ObjectUtil.isNull(model)) {
throw new ServiceException("流程模型不存在!");
}
if (StrUtil.isBlank(wfModelDTO.getBpmnXml())) {
throw new RuntimeException("获取模型设计图失败!");
}
BpmnModel bpmnModel = ModelUtils.getBpmnModel(wfModelDTO.getBpmnXml());
if (ObjectUtil.isNull(bpmnModel)) {
throw new RuntimeException("请检查流程设计图!");
}
// 获取开始节点,因为在设计的时候必须要进行表单的绑定,表单的id会在开始节点中进行属性的添加比如: <bpmn2:startEvent id="Event_11o3kf7" name="开始节点" flowable:formKey="key_1827225021857669121">,这个key_1827225021857669121就是key_表单ID
StartEvent startEvent = ModelUtils.getStartEvent(bpmnModel);
if (ObjectUtil.isNull(startEvent)) {
throw new RuntimeException("开始节点不存在,请检查流程设计是否有误!");
}
// 获取表单id
String formKey = startEvent.getFormKey();
if (StrUtil.isBlank(formKey)) {
throw new RuntimeException("请配置流程表单");
}
Model newModel = model;
// 如果是第一次设计图,流程版本不新增
if (Boolean.TRUE.equals(wfModelDTO.getNewVersion())) {
// 如果当前版本不是1,创建新版本
newModel = repositoryService.newModel();
newModel.setName(model.getName());
newModel.setKey(model.getKey());
newModel.setCategory(model.getCategory());
Integer latestVersion = model.getVersion() + 1;
ModelMetaInfoBO modelMetaInfoBO = JSON.parseObject(model.getMetaInfo(), ModelMetaInfoBO.class);
long formId = Long.parseLong(StrUtil.removePrefix(formKey, "key_"));
modelMetaInfoBO.setFormId(formId);
newModel.setMetaInfo(rebuildMetaInfo(modelMetaInfoBO, latestVersion));
newModel.setVersion(latestVersion);
}
repositoryService.saveModel(newModel);
// 保存流程图
byte[] xmlByte = StrUtil.bytes(wfModelDTO.getBpmnXml(), CharsetUtil.UTF_8);
repositoryService.addModelEditorSource(newModel.getId(), xmlByte);
}
历史
每一次的修改都要进行一次版本号的的变更,我们需要看到所有的历史设计
@Override
public TableDataInfo<WfModelVo> historyList(WfModelDTO wfModelDTO, PageQuery pageQuery) {
// 根据当前模型的key构建模型查询器,并对版本号倒序
ModelQuery modelQuery = repositoryService.createModelQuery()
.modelKey(wfModelDTO.getKey())
.orderByModelVersion()
.desc();
// TODO: 目前还无法实现在历史模型中进行条件检索,因为如果在modelQuery中添加检索条件,可能会导致排除的第一条记录不是最新的记录
// 去掉最新版
long pageTotal = modelQuery.count() - 1;
if (pageTotal <= 0) {
return TableDataInfo.build();
}
int offset = (pageQuery.getPageNum() - 1) * pageQuery.getPageSize();
// 如果是第一页,则跳过第一条记录
if (pageQuery.getPageNum() == 1) {
offset += 1;
}
int pageSize = pageQuery.getPageSize();
// 获取分页数据
List<Model> models = modelQuery.listPage(offset, pageSize);
// 构建WfModelVo列表
List<WfModelVo> list = buildModelVo(models, false);
// 创建分页对象
Page<WfModelVo> page = new Page<>();
page.setTotal(pageTotal);
page.setRecords(list);
// 构建TableDataInfo对象并返回
return TableDataInfo.build(page);
}
/**
* 构建model对象
*
* @param models model集合
* @param bpmnXml 是否需要封装bpmnXml
* @return 模型集合
*/
private List<WfModelVo> buildModelVo(List<Model> models, boolean bpmnXml) {
return models.stream().map(model -> {
WfModelVo wfModelVo = new WfModelVo();
// 设置模型ID
wfModelVo.setModelId(model.getId());
// 设置模型名称
wfModelVo.setModelName(model.getName());
// 设置模型键
wfModelVo.setModelKey(model.getKey());
// 设置模型分类
wfModelVo.setCategory(model.getCategory());
// 设置模型版本
wfModelVo.setVersion(model.getVersion());
String metaInfo = model.getMetaInfo();
if (StrUtil.isNotBlank(metaInfo)) {
// 解析元数据信息
ModelMetaInfoBO modelMetaInfoBO = JSON.parseObject(metaInfo, ModelMetaInfoBO.class);
// 设置模型描述
wfModelVo.setDescription(modelMetaInfoBO.getDesc());
// 设置表单ID
wfModelVo.setFormId(modelMetaInfoBO.getFormId());
// 设置创建时间
wfModelVo.setCreateTime(modelMetaInfoBO.getCreateTime());
// 设置更新时间
wfModelVo.setUpdateTime(modelMetaInfoBO.getUpdateTime());
// 设置创建用户
wfModelVo.setCreateUser(modelMetaInfoBO.getCreateUser());
// 设置更新用户
wfModelVo.setUpdateUser(modelMetaInfoBO.getUpdateUser());
}
if (bpmnXml) {
// 获取模型编辑器的源数据
byte[] modelEditorSource = repositoryService.getModelEditorSource(model.getId());
// 将字节数组转换为UTF-8字符串
String bpmn = StrUtil.utf8Str(modelEditorSource);
// 设置BPMN XML
wfModelVo.setBpmnXml(bpmn);
}
return wfModelVo;
}).collect(Collectors.toList());
}
最新
根据历史列表中的模型,选择一个进行设置为最新版本。

@Override
@Transactional
public void latest(String modelId) {
// 获取模型
Model model = repositoryService.getModel(modelId);
if (ObjectUtil.isNull(model)) {
throw new ServiceException("流程模型不存在!");
}
// 获取最新的模型版本号,模型的key是不变的
String modelKey = model.getKey();
Model latestModel = repositoryService.createModelQuery().modelKey(modelKey).latestVersion().singleResult();
// 判断当前模型是不是最新的
if (model.getVersion().equals(latestModel.getVersion())) {
throw new ServiceException("当前模型就是最新版本!");
}
// 最新版本号
Integer latestVersion = latestModel.getVersion() + 1;
// 将id设置为空,保存为新模型
Model newModel = repositoryService.newModel();
newModel.setName(model.getName());
newModel.setKey(model.getKey());
newModel.setCategory(model.getCategory());
newModel.setVersion(latestVersion);
newModel.setMetaInfo(model.getName());
String metaInfo = model.getMetaInfo();
ModelMetaInfoBO modelMetaInfoBO = JSON.parseObject(metaInfo, ModelMetaInfoBO.class);
newModel.setMetaInfo(rebuildMetaInfo(modelMetaInfoBO, latestVersion));
// 保存流程模型
repositoryService.saveModel(newModel);
}
/**
* 重构MetaInfo
*
* @param modelMetaInfoBO 模型元数据对象
* @param latestVersion 最新版本
* @return {@link ModelMetaInfoBO}
*/
private static String rebuildMetaInfo(ModelMetaInfoBO modelMetaInfoBO, Integer latestVersion) {
if (ObjectUtil.isNull(modelMetaInfoBO)) {
return "{}";
}
modelMetaInfoBO.setVersion(latestVersion);
modelMetaInfoBO.setUpdateTime(new Date());
modelMetaInfoBO.setUpdateUser("admin");
return JSON.toJSONString(modelMetaInfoBO);
}
部署
@Override
public boolean deploy(String modelId) {
Model model = repositoryService.getModel(modelId);
if (ObjectUtil.isNull(model)) {
throw new ServiceException("流程模型不存在!");
}
// 以字节数组返回流程设计图
byte[] bpmXmlByte = repositoryService.getModelEditorSource(modelId);
if (ArrayUtil.isEmpty(bpmXmlByte)) {
throw new ServiceException("请先设计流程图!");
}
// 将字节数组转换为UTF-8编码的字符串
String xml = StrUtil.utf8Str(bpmXmlByte);
// 解析流程设计图的字符串,获取BpmnModel对象
BpmnModel bpmnModel = ModelUtils.getBpmnModel(xml);
// 获取流程名称,并加上后缀
String processName = model.getName() + ProcessConstants.SUFFIX;
// 创建部署对象,并设置相关属性
Deployment deployment = repositoryService.createDeployment()
.category(model.getCategory())
.name(model.getName())
.key(model.getKey())
.addBpmnModel(processName, bpmnModel)
.deploy();
// 创建一个查询流程对象,根据部署ID查询,如果不构建这个对象,以后部署的流程无法查询
ProcessDefinition processDefinition = repositoryService.createProcessDefinitionQuery()
.deploymentId(deployment.getId())
.singleResult();
// 修改流程定义的分类,以便于搜索
repositoryService.setProcessDefinitionCategory(processDefinition.getId(), model.getCategory());
// 设置流程发起的权限,这里都是假数据
// 用户
repositoryService.addCandidateStarterUser(processDefinition.getId(), "123L");
// 组
repositoryService.addCandidateStarterGroup(processDefinition.getId(), "321L");
// 保存部署表单
return wfDeployService.saveInternalDeployForm(deployment.getId(), bpmnModel);
}
/**
* 保存内部部署表单信息,这里不使用flowable提供的form表,需要自己创建两张表 tb_form、tb_deploy_form
*
* @param deployId 部署ID
* @param bpmnModel BPMN模型
* @return 保存是否成功
* @throws ServiceException 如果开始节点不存在,抛出此异常
* @Transactional(rollbackFor = Exception.class) 如果在执行方法时发生异常,则回滚事务
*/
@Override
@Transactional(rollbackFor = Exception.class)
public boolean saveInternalDeployForm(String deployId, BpmnModel bpmnModel) {
// 获取每个节点上的form表单ID进行保存
List<WfDeployForm> deployFormList = new ArrayList<>();
// 获取开始节点
StartEvent startEvent = ModelUtils.getStartEvent(bpmnModel);
if (ObjectUtil.isNull(startEvent)) {
throw new ServiceException("开始节点不存在,请检查流程设计是否有误!");
}
// 保存开始节点表单信息
WfDeployForm startDeployForm = buildDeployForm(deployId, startEvent);
if (ObjectUtil.isNotNull(startDeployForm)) {
deployFormList.add(startDeployForm);
}
// 保存用户节点表单信息
Collection<UserTask> userTasks = ModelUtils.getAllUserTaskEvent(bpmnModel);
if (CollUtil.isNotEmpty(userTasks)) {
for (UserTask userTask : userTasks) {
WfDeployForm userTaskDeployForm = buildDeployForm(deployId, userTask);
if (ObjectUtil.isNotNull(userTaskDeployForm)) {
deployFormList.add(userTaskDeployForm);
}
}
}
// 批量新增部署流程和表单关联信息
return wfDeployFormMapper.insertBatch(deployFormList);
}
修改
@Override
@Transactional
public void update(WfModelDTO wfModelDTO) {
String modelId = wfModelDTO.getId();
Model model = repositoryService.getModel(modelId);
if (ObjectUtil.isNull(model)) {
throw new ServiceException("流程模型不存在!");
}
// 将wfModelDTO的属性复制到model对象中,忽略key属性
BeanUtil.copyProperties(wfModelDTO, model, CopyOptions.create(Model.class, true, "key"));
// 获取model的元数据信息
// 这里并不设计流程编辑图编辑,版本不增加
String metaInfo = model.getMetaInfo();
ModelMetaInfoBO modelMetaInfoBO = JSON.parseObject(metaInfo, ModelMetaInfoBO.class);
// 如果wfModelDTO的desc不为空,则更新modelMetaInfoBO的desc
if (StrUtil.isNotBlank(wfModelDTO.getDesc())) {
modelMetaInfoBO.setDesc(wfModelDTO.getDesc());
}
// 如果wfModelDTO的category不为空,则更新modelMetaInfoBO的category
if (StrUtil.isNotBlank(wfModelDTO.getCategory())) {
modelMetaInfoBO.setCategory(wfModelDTO.getCategory());
}
// 设置更新用户为"admin"
modelMetaInfoBO.setUpdateUser("admin");
// 设置更新时间为当前时间
modelMetaInfoBO.setUpdateTime(new Date());
// 将更新后的modelMetaInfoBO转为JSON字符串,并设置给model的metaInfo属性
model.setMetaInfo(JSON.toJSONString(modelMetaInfoBO));
// 保存更新后的model对象
repositoryService.saveModel(model);
}
删除
@Transactional
@Override
public void deleteByIds(List<String> ids) {
for (String id : ids) {
Model model = repositoryService.getModel(id);
if (ObjectUtil.isNull(model)) {
throw new ServiceException("流程模型不存在!");
}
repositoryService.deleteModel(id);
}
}
查询
@Override
public TableDataInfo<WfModelVo> list(WfModelDTO wfModelDTO, PageQuery pageQuery) {
// 构建一个查询对象,查询最新的版本,历史版本在单独的历史分支查看
ModelQuery modelQuery = repositoryService.createModelQuery().latestVersion();
// 按照创建时间降序排列
modelQuery.orderByCreateTime().desc();
// 构建查询条件
// 如果流程模型key不为空,则添加查询条件
if (StrUtil.isNotBlank(wfModelDTO.getKey())) {
modelQuery.modelKey(wfModelDTO.getKey());
}
// 如果流程模型名称不为空,则添加模糊查询条件
if (StrUtil.isNotBlank(wfModelDTO.getName())) {
modelQuery.modelNameLike("%" + wfModelDTO.getName() + "%");
}
// 如果流程模型分类不为空,则添加模糊查询条件
if (StrUtil.isNotBlank(wfModelDTO.getCategory())) {
modelQuery.modelCategoryLike("%" + wfModelDTO.getCategory() + "%");
}
// 查询总数
long count = modelQuery.count();
// 如果没有记录,则返回空的TableDataInfo对象
if (count <= 0) {
return TableDataInfo.build();
}
// 计算偏移量
int offset = (pageQuery.getPageNum() - 1) * pageQuery.getPageSize();
// 执行分页查询
List<Model> models = modelQuery.listPage(offset, pageQuery.getPageSize());
// 将查询结果转换为WfModelVo列表
List<WfModelVo> modelVos = buildModelVo(models, false);
// 创建分页对象,并设置总记录数和记录列表
Page<WfModelVo> page = new Page<>();
page.setTotal(count);
page.setRecords(modelVos);
// 返回包含分页信息的TableDataInfo对象
return TableDataInfo.build(page);
}
/**
* 构建model对象
*
* @param models model集合
* @param bpmnXml 是否需要封装bpmnXml
* @return 模型集合
*/
private List<WfModelVo> buildModelVo(List<Model> models, boolean bpmnXml) {
return models.stream().map(model -> {
WfModelVo wfModelVo = new WfModelVo();
// 设置模型信息
// 设置模型ID
wfModelVo.setModelId(model.getId());
// 设置模型名称
wfModelVo.setModelName(model.getName());
// 设置模型键
wfModelVo.setModelKey(model.getKey());
// 设置模型分类
wfModelVo.setCategory(model.getCategory());
// 设置模型版本
wfModelVo.setVersion(model.getVersion());
String metaInfo = model.getMetaInfo();
if (StrUtil.isNotBlank(metaInfo)) {
// 解析元数据信息
ModelMetaInfoBO modelMetaInfoBO = JSON.parseObject(metaInfo, ModelMetaInfoBO.class);
// 设置模型详细信息
// 设置模型描述
wfModelVo.setDescription(modelMetaInfoBO.getDesc());
// 设置表单ID
wfModelVo.setFormId(modelMetaInfoBO.getFormId());
// 设置创建时间
wfModelVo.setCreateTime(modelMetaInfoBO.getCreateTime());
// 设置更新时间
wfModelVo.setUpdateTime(modelMetaInfoBO.getUpdateTime());
// 设置创建用户
wfModelVo.setCreateUser(modelMetaInfoBO.getCreateUser());
// 设置更新用户
wfModelVo.setUpdateUser(modelMetaInfoBO.getUpdateUser());
}
if (bpmnXml) {
// 如果需要封装bpmnXml,则执行以下操作
// 获取模型编辑器的源数据
byte[] modelEditorSource = repositoryService.getModelEditorSource(model.getId());
// 将字节数组转换为UTF-8字符串
String bpmn = StrUtil.utf8Str(modelEditorSource);
// 设置BPMN XML
wfModelVo.setBpmnXml(bpmn);
}
return wfModelVo;
}).collect(Collectors.toList());
}
流程部署

查询
@Override
public TableDataInfo<WfDeployVo> queryPageList(ProcessQuery processQuery, PageQuery pageQuery) {
// 比如这里查询出来用户的id和组
String userId = "123L";
String groupId = "321L";
// 创建查询对象
ProcessDefinitionQuery processDefinitionQuery = repositoryService.createProcessDefinitionQuery()
.startableByUserOrGroups(userId, Collections.singleton(groupId))
.latestVersion()
.orderByProcessDefinitionKey()
.desc();
// 封装查询条件
ProcessUtils.buildProcessSearch(processDefinitionQuery, processQuery);
long total = processDefinitionQuery.count();
if (total <= 0) {
return TableDataInfo.build();
}
int offset = pageQuery.getPageSize() * (pageQuery.getPageNum() - 1);
int pageSize = pageQuery.getPageSize();
List<ProcessDefinition> processDefinitionList = processDefinitionQuery.listPage(offset, pageSize);
// 批量查询部署流程
List<Deployment> deployments = repositoryService.createDeploymentQuery().deploymentIds(processDefinitionList.stream().map(ProcessDefinition::getDeploymentId).collect(Collectors.toList())).list();
List<WfDeployVo> list = processDefinitionList.stream().map(processDefinition -> {
// 查询部署
WfDeployVo wfDeployVo = new WfDeployVo();
wfDeployVo.setProcessDefId(processDefinition.getId());
wfDeployVo.setProcessDefKey(processDefinition.getKey());
wfDeployVo.setProcessDefName(processDefinition.getName());
wfDeployVo.setVersion(processDefinition.getVersion());
wfDeployVo.setCategory(processDefinition.getCategory());
wfDeployVo.setDeployId(processDefinition.getDeploymentId());
wfDeployVo.setSuspended(processDefinition.isSuspended());
Deployment deployment = CollUtil.findOne(deployments, d -> d.getId().equals(processDefinition.getDeploymentId()));
wfDeployVo.setDeployTime(deployment.getDeploymentTime());
return wfDeployVo;
}).collect(Collectors.toList());
Page<WfDeployVo> page = new Page();
page.setTotal(total);
page.setRecords(list);
return TableDataInfo.build(page);
}
版本管理

// 这里会一并查询出当前部署的所有版本信息,包括最新的
@Override
public TableDataInfo<WfDeployVo> queryPublishList(String processKey, PageQuery pageQuery) {
ProcessDefinitionQuery processDefinitionQuery = repositoryService.createProcessDefinitionQuery()
.processDefinitionKey(processKey)
.orderByProcessDefinitionVersion()
.desc();
long total = processDefinitionQuery.count();
if (total <= 0) {
return TableDataInfo.build();
}
// 根据查询条件,查询所有版本
int offset = pageQuery.getPageSize() * (pageQuery.getPageNum() - 1);
int pageSize = pageQuery.getPageSize();
List<ProcessDefinition> list = processDefinitionQuery.listPage(offset, pageSize);
//查询部署信息
List<Deployment> deployments = repositoryService.createDeploymentQuery().deploymentIds(list.stream().map(ProcessDefinition::getDeploymentId).collect(Collectors.toList())).list();
if (CollUtil.isEmpty(deployments)) {
throw new ServiceException("部署信息有误!");
}
Map<String, Date> dateMap = deployments.stream().collect(Collectors.toMap(deployment -> deployment.getId(), deployment -> deployment.getDeploymentTime()));
List<WfDeployVo> wfDeployVos = list.stream().map(item -> {
WfDeployVo vo = new WfDeployVo();
vo.setProcessDefId(item.getId());
vo.setProcessDefKey(item.getKey());
vo.setProcessDefName(item.getName());
vo.setVersion(item.getVersion());
vo.setCategory(item.getCategory());
vo.setDeployId(item.getDeploymentId());
vo.setSuspended(item.isSuspended());
vo.setDeployTime(dateMap.get(item.getDeploymentId()));
return vo;
}).collect(Collectors.toList());
Page<WfDeployVo> page = new Page<>();
page.setRecords(wfDeployVos);
page.setTotal(total);
return TableDataInfo.build(page);
}
删除
/**
* 根据部署ID列表删除部署
*
* @param list 部署ID列表
* @return 无返回值
*/
@Override
@Transactional
public void deleteByIds(List<String> list) {
for (String deployId : list) {
repositoryService.deleteDeployment(deployId, true);
}
}
激活/挂起
/**
* 更新流程定义状态
*
* @param definitionId 流程定义ID
* @param state 流程定义状态(ACTIVE激活或SUSPENDED挂起)
* @throws IllegalArgumentException 如果state参数不为ACTIVE或SUSPENDED则抛出此异常
*/
@Override
@Transactional
public void updateState(String definitionId, String state) {
if (SuspensionState.ACTIVE.toString().equals(state)) {
// 激活
repositoryService.activateProcessDefinitionById(definitionId, true, null);
} else if (SuspensionState.SUSPENDED.toString().equals(state)) {
// 挂起
repositoryService.suspendProcessDefinitionById(definitionId, true, null);
}
}
新建流程
查询

/**
* 查询流程定义列表,这里要查询最新版本并且是有发起权限的流程
*
* @param processQuery 流程查询对象
* @param pageQuery 分页查询对象
* @return 流程定义列表
*/
@Override
public TableDataInfo<WfDefinitionVo> selectPageStartProcessList(ProcessQuery processQuery, PageQuery pageQuery) {
String userId = "123L";
String groupId = "321L";
// 创建查询对象
ProcessDefinitionQuery processDefinitionQuery = repositoryService.createProcessDefinitionQuery()
.latestVersion() //最新版本
.active() //活跃状态
.orderByProcessDefinitionKey() //根据标识排序
.startableByUserOrGroups(userId, Collections.singleton(groupId)) //权限
.desc();//倒叙
// 构建查询参数
ProcessUtils.buildProcessSearch(processDefinitionQuery, processQuery);
long pageTotal = processDefinitionQuery.count();
if (pageTotal <= 0) {
return TableDataInfo.build();
}
int pageSize = pageQuery.getPageSize();
int offset = pageSize * (pageQuery.getPageNum() - 1);
List<ProcessDefinition> processDefinitions = processDefinitionQuery.listPage(offset, pageSize);
// 查询部署时间
Map<String, Date> deployMap = new HashMap<>();
List<Deployment> deployments = repositoryService.createDeploymentQuery().deploymentIds(processDefinitions.stream().map(ProcessDefinition::getDeploymentId).collect(Collectors.toList())).list();
if (CollectionUtil.isNotEmpty(deployments)) {
deployMap = CollUtil.toMap(deployments, deployMap, Deployment::getId, Deployment::getDeploymentTime);
}
// 封装对象
List<WfDefinitionVo> wfDefinitionVos = new ArrayList<>();
for (ProcessDefinition processDefinition : processDefinitions) {
WfDefinitionVo vo = new WfDefinitionVo();
vo.setProcessDefinitionId(processDefinition.getId());
vo.setProcessDefinitionKey(processDefinition.getKey());
vo.setProcessDefinitionName(processDefinition.getName());
vo.setVersion(processDefinition.getVersion());
vo.setCategory(processDefinition.getCategory());
vo.setSuspended(processDefinition.isSuspended());
vo.setDeploymentTime(deployMap.get(processDefinition.getDeploymentId()));
wfDefinitionVos.add(vo);
}
Page<WfDefinitionVo> page = new Page<>();
page.setTotal(pageTotal);
page.setRecords(wfDefinitionVos);
return TableDataInfo.build(page);
}
发起
点击发起后,需要填写表单内容需要先查询出表单数据。

/**
* 根据流程定义ID、部署ID和流程实例ID查询对应的流程表单内容
*
* @param definitionId 流程定义ID
* @param deployId 部署ID
* @param procInsId 流程实例ID
* @return 流程表单对象
*/
@Override
public ProcessFormVo selectFormContent(String definitionId, String deployId, String procInsId) {
// 初始化流程表单对象
ProcessFormVo processFormVo = new ProcessFormVo();
// 根据流程定义ID获取BPMN模型
BpmnModel bpmnModel = repositoryService.getBpmnModel(definitionId);
// 如果BPMN模型为空,则抛出异常
if (ObjectUtil.isNull(bpmnModel)) throw new ServiceException("流程图设计为空!");
// 从BPMN模型中获取开始事件
StartEvent startEvent = ModelUtils.getStartEvent(bpmnModel);
// 如果开始事件为空,则抛出异常
if (ObjectUtil.isNull(startEvent)) throw new ServiceException("流程图中没有开始节点,请检查流程设计!");
// 根据部署ID和开始事件的信息查询对应的流程表单
WfDeployForm wfDeployForm = wfDeployFormMapper.selectOne(new LambdaQueryWrapper<>(WfDeployForm.class)
.eq(WfDeployForm::getDeployId, deployId)
.eq(WfDeployForm::getFormKey, startEvent.getFormKey())
.eq(WfDeployForm::getNodeKey, startEvent.getId()));
// 如果查询到的流程表单为空,则抛出异常
if (ObjectUtil.isNull(wfDeployForm)) throw new ServiceException("流程表单为空!");
// 获取流程表单的内容
String formContent = wfDeployForm.getContent();
// 如果表单内容为空或仅包含空白字符,则抛出异常
if (StrUtil.isBlank(formContent)) throw new ServiceException("获取流程表单失败!");
// 将表单内容解析为Map对象
Map<String, Object> formModel = JSON.parseObject(formContent, Map.class);
// 设置表单按钮的显示状态为不显示
processFormVo.setFormBtns(false);
// 设置表单模型
processFormVo.setFormModel(formModel);
// 如果流程实例ID不为空,则执行以下逻辑
if (StrUtil.isNotBlank(procInsId)) {
// 根据流程实例ID查询历史流程实例信息,并包含流程变量
HistoricProcessInstance historicProcIns = historyService.createHistoricProcessInstanceQuery()
.processInstanceId(procInsId)
.includeProcessVariables()
.singleResult();
// 设置流程表单的数据为历史流程实例中的流程变量
processFormVo.setFormData(historicProcIns.getProcessVariables());
}
// 返回流程表单对象
return processFormVo;
}