refactor(flowable): 移除自定义流程图配置和相关工具类

- 删除 application.yml 中的 Flowable 配置项
- 移除 CustomProcessDiagramCanvas 自定义流程图绘制类
- 移除 CustomProcessDiagramGenerator 自定义流程图生成器
- 移除 FindNextNodeUtil 流程节点查找工具类
- 移除 FlowableConfig 配置类
This commit is contained in:
2025-12-29 16:21:33 +08:00
parent 9b8c5ab80b
commit ee88de3e99
89 changed files with 0 additions and 9274 deletions

View File

@@ -78,12 +78,6 @@
<artifactId>klp-demo</artifactId>
</dependency>
<!-- flowable模块-->
<dependency>
<groupId>com.klp</groupId>
<artifactId>klp-flowable</artifactId>
</dependency>
<!-- wms模块-->
<dependency>
<groupId>com.klp</groupId>

View File

@@ -1,117 +0,0 @@
package com.klp.web.controller.workflow;
import cn.dev33.satoken.annotation.SaCheckLogin;
import cn.dev33.satoken.annotation.SaCheckPermission;
import com.klp.common.annotation.Log;
import com.klp.common.annotation.RepeatSubmit;
import com.klp.common.core.controller.BaseController;
import com.klp.common.core.domain.PageQuery;
import com.klp.common.core.domain.R;
import com.klp.common.core.page.TableDataInfo;
import com.klp.common.enums.BusinessType;
import com.klp.common.utils.poi.ExcelUtil;
import com.klp.workflow.domain.WfCategory;
import com.klp.workflow.domain.vo.WfCategoryVo;
import com.klp.workflow.service.IWfCategoryService;
import lombok.RequiredArgsConstructor;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.*;
import javax.servlet.http.HttpServletResponse;
import javax.validation.constraints.NotEmpty;
import javax.validation.constraints.NotNull;
import java.util.Arrays;
import java.util.List;
/**
* 流程分类Controller
*
* @author KonBAI
* @createTime 2022/3/10 00:12
*/
@Validated
@RequiredArgsConstructor
@RestController
@RequestMapping("/workflow/category")
public class WfCategoryController extends BaseController {
private final IWfCategoryService categoryService;
/**
* 查询流程分类列表
*/
@SaCheckPermission("workflow:category:list")
@GetMapping("/list")
public TableDataInfo<WfCategoryVo> list(WfCategory category, PageQuery pageQuery) {
return categoryService.queryPageList(category, pageQuery);
}
/**
* 查询全部的流程分类列表
*/
@SaCheckLogin
@GetMapping("/listAll")
public R<List<WfCategoryVo>> listAll(WfCategory category) {
return R.ok(categoryService.queryList(category));
}
/**
* 导出流程分类列表
*/
@SaCheckPermission("workflow:category:export")
@Log(title = "流程分类", businessType = BusinessType.EXPORT)
@PostMapping("/export")
public void export(@Validated WfCategory category, HttpServletResponse response) {
List<WfCategoryVo> list = categoryService.queryList(category);
ExcelUtil.exportExcel(list, "流程分类", WfCategoryVo.class, response);
}
/**
* 获取流程分类详细信息
* @param categoryId 分类主键
*/
@SaCheckPermission("workflow:category:query")
@GetMapping("/{categoryId}")
public R<WfCategoryVo> getInfo(@NotNull(message = "主键不能为空") @PathVariable("categoryId") Long categoryId) {
return R.ok(categoryService.queryById(categoryId));
}
/**
* 新增流程分类
*/
@SaCheckPermission("workflow:category:add")
@Log(title = "流程分类", businessType = BusinessType.INSERT)
@RepeatSubmit()
@PostMapping()
public R<Void> add(@Validated @RequestBody WfCategory category) {
if (!categoryService.checkCategoryCodeUnique(category)) {
return R.fail("新增流程分类'" + category.getCategoryName() + "'失败,流程编码已存在");
}
return toAjax(categoryService.insertCategory(category));
}
/**
* 修改流程分类
*/
@SaCheckPermission("workflow:category:edit")
@Log(title = "流程分类", businessType = BusinessType.UPDATE)
@RepeatSubmit()
@PutMapping()
public R<Void> edit(@Validated @RequestBody WfCategory category) {
if (!categoryService.checkCategoryCodeUnique(category)) {
return R.fail("修改流程分类'" + category.getCategoryName() + "'失败,流程编码已存在");
}
return toAjax(categoryService.updateCategory(category));
}
/**
* 删除流程分类
* @param categoryIds 分类主键串
*/
@SaCheckPermission("workflow:category:remove")
@Log(title = "流程分类" , businessType = BusinessType.DELETE)
@DeleteMapping("/{categoryIds}")
public R<Void> remove(@NotEmpty(message = "主键不能为空") @PathVariable Long[] categoryIds) {
return toAjax(categoryService.deleteWithValidByIds(Arrays.asList(categoryIds), true));
}
}

View File

@@ -1,107 +0,0 @@
package com.klp.web.controller.workflow;
import cn.dev33.satoken.annotation.SaCheckPermission;
import com.klp.common.annotation.Log;
import com.klp.common.core.controller.BaseController;
import com.klp.common.core.domain.PageQuery;
import com.klp.common.core.domain.R;
import com.klp.common.core.page.TableDataInfo;
import com.klp.common.enums.BusinessType;
import com.klp.common.utils.JsonUtils;
import com.klp.flowable.core.domain.ProcessQuery;
import com.klp.workflow.domain.vo.WfDeployVo;
import com.klp.workflow.domain.vo.WfFormVo;
import com.klp.workflow.service.IWfDeployFormService;
import com.klp.workflow.service.IWfDeployService;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.web.bind.annotation.*;
import javax.validation.constraints.NotEmpty;
import java.util.Arrays;
import java.util.Map;
import java.util.Objects;
/**
* 流程部署
*
* @author KonBAI
* @createTime 2022/3/24 20:57
*/
@Slf4j
@RequiredArgsConstructor
@RestController
@RequestMapping("/workflow/deploy")
public class WfDeployController extends BaseController {
private final IWfDeployService deployService;
private final IWfDeployFormService deployFormService;
/**
* 查询流程部署列表
*/
@SaCheckPermission("workflow:deploy:list")
@GetMapping("/list")
public TableDataInfo<WfDeployVo> list(ProcessQuery processQuery, PageQuery pageQuery) {
return deployService.queryPageList(processQuery, pageQuery);
}
/**
* 查询流程部署版本列表
*/
@SaCheckPermission("workflow:deploy:list")
@GetMapping("/publishList")
public TableDataInfo<WfDeployVo> publishList(@RequestParam String processKey, PageQuery pageQuery) {
return deployService.queryPublishList(processKey, pageQuery);
}
/**
* 激活或挂起流程
*
* @param state 状态active:激活 suspended:挂起)
* @param definitionId 流程定义ID
*/
@SaCheckPermission("workflow:deploy:state")
@PutMapping(value = "/changeState")
public R<Void> changeState(@RequestParam String state, @RequestParam String definitionId) {
deployService.updateState(definitionId, state);
return R.ok();
}
/**
* 读取xml文件
* @param definitionId 流程定义ID
* @return
*/
@SaCheckPermission("workflow:deploy:query")
@GetMapping("/bpmnXml/{definitionId}")
public R<String> getBpmnXml(@PathVariable(value = "definitionId") String definitionId) {
return R.ok(null, deployService.queryBpmnXmlById(definitionId));
}
/**
* 删除流程模型
* @param deployIds 流程部署ids
*/
@SaCheckPermission("workflow:deploy:remove")
@Log(title = "删除流程部署", businessType = BusinessType.DELETE)
@DeleteMapping("/{deployIds}")
public R<String> remove(@NotEmpty(message = "主键不能为空") @PathVariable String[] deployIds) {
deployService.deleteByIds(Arrays.asList(deployIds));
return R.ok();
}
/**
* 查询流程部署关联表单信息
*
* @param deployId 流程部署id
*/
@GetMapping("/form/{deployId}")
public R<?> start(@PathVariable(value = "deployId") String deployId) {
WfFormVo formVo = deployFormService.selectDeployFormByDeployId(deployId);
if (Objects.isNull(formVo)) {
return R.fail("请先配置流程表单");
}
return R.ok(JsonUtils.parseObject(formVo.getContent(), Map.class));
}
}

View File

@@ -1,112 +0,0 @@
package com.klp.web.controller.workflow;
import cn.dev33.satoken.annotation.SaCheckPermission;
import com.klp.common.annotation.Log;
import com.klp.common.core.controller.BaseController;
import com.klp.common.core.domain.PageQuery;
import com.klp.common.core.domain.R;
import com.klp.common.core.page.TableDataInfo;
import com.klp.common.core.validate.QueryGroup;
import com.klp.common.enums.BusinessType;
import com.klp.common.utils.poi.ExcelUtil;
import com.klp.workflow.domain.WfDeployForm;
import com.klp.workflow.domain.bo.WfFormBo;
import com.klp.workflow.domain.vo.WfFormVo;
import com.klp.workflow.service.IWfDeployFormService;
import com.klp.workflow.service.IWfFormService;
import lombok.RequiredArgsConstructor;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.*;
import javax.servlet.http.HttpServletResponse;
import javax.validation.constraints.NotEmpty;
import javax.validation.constraints.NotNull;
import java.util.Arrays;
import java.util.List;
/**
* 流程表单Controller
*
* @author KonBAI
* @createTime 2022/3/7 22:07
*/
@RequiredArgsConstructor
@RestController
@RequestMapping("/workflow/form")
public class WfFormController extends BaseController {
private final IWfFormService formService;
private final IWfDeployFormService deployFormService;
/**
* 查询流程表单列表
*/
@SaCheckPermission("workflow:form:list")
@GetMapping("/list")
public TableDataInfo<WfFormVo> list(@Validated(QueryGroup.class) WfFormBo bo, PageQuery pageQuery) {
return formService.queryPageList(bo, pageQuery);
}
/**
* 导出流程表单列表
*/
@SaCheckPermission("workflow:form:export")
@Log(title = "流程表单", businessType = BusinessType.EXPORT)
@PostMapping("/export")
public void export(@Validated WfFormBo bo, HttpServletResponse response) {
List<WfFormVo> list = formService.queryList(bo);
ExcelUtil.exportExcel(list, "流程表单", WfFormVo.class, response);
}
/**
* 获取流程表单详细信息
* @param formId 主键
*/
@SaCheckPermission("workflow:form:query")
@GetMapping(value = "/{formId}")
public R<WfFormVo> getInfo(@NotNull(message = "主键不能为空") @PathVariable("formId") Long formId) {
return R.ok(formService.queryById(formId));
}
/**
* 新增流程表单
*/
@SaCheckPermission("workflow:form:add")
@Log(title = "流程表单", businessType = BusinessType.INSERT)
@PostMapping
public R<Void> add(@RequestBody WfFormBo bo) {
return toAjax(formService.insertForm(bo));
}
/**
* 修改流程表单
*/
@SaCheckPermission("workflow:form:edit")
@Log(title = "流程表单", businessType = BusinessType.UPDATE)
@PutMapping
public R<Void> edit(@RequestBody WfFormBo bo) {
return toAjax(formService.updateForm(bo));
}
/**
* 删除流程表单
* @param formIds 主键串
*/
@SaCheckPermission("workflow:form:remove")
@Log(title = "流程表单", businessType = BusinessType.DELETE)
@DeleteMapping("/{formIds}")
public R<Void> remove(@NotEmpty(message = "主键不能为空") @PathVariable Long[] formIds) {
return toAjax(formService.deleteWithValidByIds(Arrays.asList(formIds)) ? 1 : 0);
}
/**
* 挂载流程表单
*/
@Log(title = "流程表单", businessType = BusinessType.INSERT)
@PostMapping("/addDeployForm")
public R<Void> addDeployForm(@RequestBody WfDeployForm deployForm) {
return toAjax(deployFormService.insertWfDeployForm(deployForm));
}
}

View File

@@ -1,71 +0,0 @@
package com.klp.web.controller.workflow;
import com.klp.common.core.domain.R;
import com.klp.workflow.domain.bo.WfTaskBo;
import com.klp.workflow.service.IWfInstanceService;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.web.bind.annotation.*;
/**
* 工作流流程实例管理
*
* @author KonBAI
* @createTime 2022/3/10 00:12
*/
@Slf4j
@RequiredArgsConstructor
@RestController
@RequestMapping("/workflow/instance")
public class WfInstanceController {
private final IWfInstanceService instanceService;
/**
* 激活或挂起流程实例
*
* @param state 1:激活,2:挂起
* @param instanceId 流程实例ID
*/
@PostMapping(value = "/updateState")
public R updateState(@RequestParam Integer state, @RequestParam String instanceId) {
instanceService.updateState(state, instanceId);
return R.ok();
}
/**
* 结束流程实例
*
* @param bo 流程任务业务对象
*/
@PostMapping(value = "/stopProcessInstance")
public R stopProcessInstance(@RequestBody WfTaskBo bo) {
instanceService.stopProcessInstance(bo);
return R.ok();
}
/**
* 删除流程实例
*
* @param instanceId 流程实例ID
* @param deleteReason 删除原因
*/
@Deprecated
@DeleteMapping(value = "/delete")
public R delete(@RequestParam String instanceId, String deleteReason) {
instanceService.delete(instanceId, deleteReason);
return R.ok();
}
/**
* 查询流程实例详情信息
*
* @param procInsId 流程实例ID
* @param deployId 流程部署ID
*/
@GetMapping("/detail")
public R detail(String procInsId, String deployId) {
return R.ok(instanceService.queryDetailProcess(procInsId, deployId));
}
}

View File

@@ -1,187 +0,0 @@
package com.klp.web.controller.workflow;
import cn.dev33.satoken.annotation.SaCheckPermission;
import cn.hutool.core.bean.BeanUtil;
import com.klp.common.annotation.Log;
import com.klp.common.annotation.RepeatSubmit;
import com.klp.common.core.controller.BaseController;
import com.klp.common.core.domain.PageQuery;
import com.klp.common.core.domain.R;
import com.klp.common.core.page.TableDataInfo;
import com.klp.common.core.validate.AddGroup;
import com.klp.common.core.validate.EditGroup;
import com.klp.common.enums.BusinessType;
import com.klp.common.utils.poi.ExcelUtil;
import com.klp.workflow.domain.WfCategory;
import com.klp.workflow.domain.bo.WfModelBo;
import com.klp.workflow.domain.vo.WfCategoryVo;
import com.klp.workflow.domain.vo.WfModelExportVo;
import com.klp.workflow.domain.vo.WfModelVo;
import com.klp.workflow.service.IWfCategoryService;
import com.klp.workflow.service.IWfModelService;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.*;
import javax.servlet.http.HttpServletResponse;
import javax.validation.constraints.NotEmpty;
import javax.validation.constraints.NotNull;
import java.util.Arrays;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;
/**
* 工作流流程模型管理
*
* @author KonBAI
* @createTime 2022/6/21 9:09
*/
@Slf4j
@RequiredArgsConstructor
@RestController
@RequestMapping("/workflow/model")
public class WfModelController extends BaseController {
private final IWfModelService modelService;
private final IWfCategoryService categoryService;
/**
* 查询流程模型列表
*
* @param modelBo 流程模型对象
* @param pageQuery 分页参数
*/
@SaCheckPermission("workflow:model:list")
@GetMapping("/list")
public TableDataInfo<WfModelVo> list(WfModelBo modelBo, PageQuery pageQuery) {
return modelService.list(modelBo, pageQuery);
}
/**
* 查询流程模型列表
*
* @param modelBo 流程模型对象
* @param pageQuery 分页参数
*/
@SaCheckPermission("workflow:model:list")
@GetMapping("/historyList")
public TableDataInfo<WfModelVo> historyList(WfModelBo modelBo, PageQuery pageQuery) {
return modelService.historyList(modelBo, pageQuery);
}
/**
* 获取流程模型详细信息
*
* @param modelId 模型主键
*/
@SaCheckPermission("workflow:model:query")
@GetMapping(value = "/{modelId}")
public R<WfModelVo> getInfo(@NotNull(message = "主键不能为空") @PathVariable("modelId") String modelId) {
return R.ok(modelService.getModel(modelId));
}
/**
* 获取流程表单详细信息
*
* @param modelId 模型主键
*/
@SaCheckPermission("workflow:model:query")
@GetMapping(value = "/bpmnXml/{modelId}")
public R<String> getBpmnXml(@NotNull(message = "主键不能为空") @PathVariable("modelId") String modelId) {
return R.ok("操作成功", modelService.queryBpmnXmlById(modelId));
}
/**
* 新增流程模型
*/
@SaCheckPermission("workflow:model:add")
@Log(title = "流程模型", businessType = BusinessType.INSERT)
@PostMapping
public R<Void> add(@Validated(AddGroup.class) @RequestBody WfModelBo modelBo) {
modelService.insertModel(modelBo);
return R.ok();
}
/**
* 修改流程模型
*/
@SaCheckPermission("workflow:model:edit")
@Log(title = "流程模型", businessType = BusinessType.UPDATE)
@PutMapping
public R<Void> edit(@Validated(EditGroup.class) @RequestBody WfModelBo modelBo) {
modelService.updateModel(modelBo);
return R.ok();
}
/**
* 保存流程模型
*/
@SaCheckPermission("workflow:model:save")
@Log(title = "保存流程模型", businessType = BusinessType.INSERT)
@RepeatSubmit()
@PostMapping("/save")
public R<String> save(@RequestBody WfModelBo modelBo) {
modelService.saveModel(modelBo);
return R.ok();
}
/**
* 设为最新流程模型
* @param modelId
* @return
*/
@SaCheckPermission("workflow:model:save")
@Log(title = "设为最新流程模型", businessType = BusinessType.INSERT)
@RepeatSubmit()
@PostMapping("/latest")
public R<?> latest(@RequestParam String modelId) {
modelService.latestModel(modelId);
return R.ok();
}
/**
* 删除流程模型
*
* @param modelIds 流程模型主键串
*/
@SaCheckPermission("workflow:model:remove")
@Log(title = "删除流程模型", businessType = BusinessType.DELETE)
@DeleteMapping("/{modelIds}")
public R<String> remove(@NotEmpty(message = "主键不能为空") @PathVariable String[] modelIds) {
modelService.deleteByIds(Arrays.asList(modelIds));
return R.ok();
}
/**
* 部署流程模型
*
* @param modelId 流程模型主键
*/
@SaCheckPermission("workflow:model:deploy")
@Log(title = "部署流程模型", businessType = BusinessType.INSERT)
@RepeatSubmit()
@PostMapping("/deploy")
public R<Void> deployModel(@RequestParam String modelId) {
return toAjax(modelService.deployModel(modelId));
}
/**
* 导出流程模型数据
*/
@Log(title = "导出流程模型数据", businessType = BusinessType.EXPORT)
@SaCheckPermission("workflow:model:export")
@PostMapping("/export")
public void export(WfModelBo modelBo, HttpServletResponse response) {
List<WfModelVo> list = modelService.list(modelBo);
List<WfModelExportVo> listVo = BeanUtil.copyToList(list, WfModelExportVo.class);
List<WfCategoryVo> categoryVos = categoryService.queryList(new WfCategory());
Map<String, String> categoryMap = categoryVos.stream()
.collect(Collectors.toMap(WfCategoryVo::getCode, WfCategoryVo::getCategoryName));
for (WfModelExportVo exportVo : listVo) {
exportVo.setCategoryName(categoryMap.get(exportVo.getCategory()));
}
ExcelUtil.exportExcel(listVo, "流程模型数据", WfModelExportVo.class, response);
}
}

View File

@@ -1,239 +0,0 @@
package com.klp.web.controller.workflow;
import cn.dev33.satoken.annotation.SaCheckPermission;
import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.util.ObjectUtil;
import com.klp.common.annotation.Log;
import com.klp.common.core.controller.BaseController;
import com.klp.common.core.domain.PageQuery;
import com.klp.common.core.domain.R;
import com.klp.common.core.page.TableDataInfo;
import com.klp.common.enums.BusinessType;
import com.klp.common.utils.poi.ExcelUtil;
import com.klp.flowable.core.domain.ProcessQuery;
import com.klp.workflow.domain.bo.WfCopyBo;
import com.klp.workflow.domain.vo.*;
import com.klp.workflow.service.IWfCopyService;
import com.klp.workflow.service.IWfProcessService;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.*;
import javax.servlet.http.HttpServletResponse;
import java.util.List;
import java.util.Map;
/**
* 工作流流程管理
*
* @author KonBAI
* @createTime 2022/3/24 18:54
*/
@Slf4j
@RequiredArgsConstructor
@RestController
@RequestMapping("/workflow/process")
public class WfProcessController extends BaseController {
private final IWfProcessService processService;
private final IWfCopyService copyService;
/**
* 查询可发起流程列表
*
* @param pageQuery 分页参数
*/
@GetMapping(value = "/list")
@SaCheckPermission("workflow:process:startList")
public TableDataInfo<WfDefinitionVo> startProcessList(ProcessQuery processQuery, PageQuery pageQuery) {
return processService.selectPageStartProcessList(processQuery, pageQuery);
}
/**
* 我拥有的流程
*/
@SaCheckPermission("workflow:process:ownList")
@GetMapping(value = "/ownList")
public TableDataInfo<WfTaskVo> ownProcessList(ProcessQuery processQuery, PageQuery pageQuery) {
return processService.selectPageOwnProcessList(processQuery, pageQuery);
}
/**
* 获取待办列表
*/
@SaCheckPermission("workflow:process:todoList")
@GetMapping(value = "/todoList")
public TableDataInfo<WfTaskVo> todoProcessList(ProcessQuery processQuery, PageQuery pageQuery) {
return processService.selectPageTodoProcessList(processQuery, pageQuery);
}
/**
* 获取待签列表
*
* @param processQuery 流程业务对象
* @param pageQuery 分页参数
*/
@SaCheckPermission("workflow:process:claimList")
@GetMapping(value = "/claimList")
public TableDataInfo<WfTaskVo> claimProcessList(ProcessQuery processQuery, PageQuery pageQuery) {
return processService.selectPageClaimProcessList(processQuery, pageQuery);
}
/**
* 获取已办列表
*
* @param pageQuery 分页参数
*/
@SaCheckPermission("workflow:process:finishedList")
@GetMapping(value = "/finishedList")
public TableDataInfo<WfTaskVo> finishedProcessList(ProcessQuery processQuery, PageQuery pageQuery) {
return processService.selectPageFinishedProcessList(processQuery, pageQuery);
}
/**
* 获取抄送列表
*
* @param copyBo 流程抄送对象
* @param pageQuery 分页参数
*/
@SaCheckPermission("workflow:process:copyList")
@GetMapping(value = "/copyList")
public TableDataInfo<WfCopyVo> copyProcessList(WfCopyBo copyBo, PageQuery pageQuery) {
copyBo.setUserId(getUserId());
return copyService.selectPageList(copyBo, pageQuery);
}
/**
* 导出可发起流程列表
*/
@SaCheckPermission("workflow:process:startExport")
@Log(title = "可发起流程", businessType = BusinessType.EXPORT)
@PostMapping("/startExport")
public void startExport(@Validated ProcessQuery processQuery, HttpServletResponse response) {
List<WfDefinitionVo> list = processService.selectStartProcessList(processQuery);
ExcelUtil.exportExcel(list, "可发起流程", WfDefinitionVo.class, response);
}
/**
* 导出我拥有流程列表
*/
@SaCheckPermission("workflow:process:ownExport")
@Log(title = "我拥有流程", businessType = BusinessType.EXPORT)
@PostMapping("/ownExport")
public void ownExport(@Validated ProcessQuery processQuery, HttpServletResponse response) {
List<WfTaskVo> list = processService.selectOwnProcessList(processQuery);
List<WfOwnTaskExportVo> listVo = BeanUtil.copyToList(list, WfOwnTaskExportVo.class);
for (WfOwnTaskExportVo exportVo : listVo) {
exportVo.setStatus(ObjectUtil.isNull(exportVo.getFinishTime()) ? "进行中" : "已完成");
}
ExcelUtil.exportExcel(listVo, "我拥有流程", WfOwnTaskExportVo.class, response);
}
/**
* 导出待办流程列表
*/
@SaCheckPermission("workflow:process:todoExport")
@Log(title = "待办流程", businessType = BusinessType.EXPORT)
@PostMapping("/todoExport")
public void todoExport(@Validated ProcessQuery processQuery, HttpServletResponse response) {
List<WfTaskVo> list = processService.selectTodoProcessList(processQuery);
List<WfTodoTaskExportVo> listVo = BeanUtil.copyToList(list, WfTodoTaskExportVo.class);
ExcelUtil.exportExcel(listVo, "待办流程", WfTodoTaskExportVo.class, response);
}
/**
* 导出待签流程列表
*/
@SaCheckPermission("workflow:process:claimExport")
@Log(title = "待签流程", businessType = BusinessType.EXPORT)
@PostMapping("/claimExport")
public void claimExport(@Validated ProcessQuery processQuery, HttpServletResponse response) {
List<WfTaskVo> list = processService.selectClaimProcessList(processQuery);
List<WfClaimTaskExportVo> listVo = BeanUtil.copyToList(list, WfClaimTaskExportVo.class);
ExcelUtil.exportExcel(listVo, "待签流程", WfClaimTaskExportVo.class, response);
}
/**
* 导出已办流程列表
*/
@SaCheckPermission("workflow:process:finishedExport")
@Log(title = "已办流程", businessType = BusinessType.EXPORT)
@PostMapping("/finishedExport")
public void finishedExport(@Validated ProcessQuery processQuery, HttpServletResponse response) {
List<WfTaskVo> list = processService.selectFinishedProcessList(processQuery);
List<WfFinishedTaskExportVo> listVo = BeanUtil.copyToList(list, WfFinishedTaskExportVo.class);
ExcelUtil.exportExcel(listVo, "已办流程", WfFinishedTaskExportVo.class, response);
}
/**
* 导出抄送流程列表
*/
@SaCheckPermission("workflow:process:copyExport")
@Log(title = "抄送流程", businessType = BusinessType.EXPORT)
@PostMapping("/copyExport")
public void copyExport(WfCopyBo copyBo, HttpServletResponse response) {
copyBo.setUserId(getUserId());
List<WfCopyVo> list = copyService.selectList(copyBo);
ExcelUtil.exportExcel(list, "抄送流程", WfCopyVo.class, response);
}
/**
* 查询流程部署关联表单信息
*
* @param definitionId 流程定义id
* @param deployId 流程部署id
*/
@GetMapping("/getProcessForm")
@SaCheckPermission("workflow:process:start")
public R<?> getForm(@RequestParam(value = "definitionId") String definitionId,
@RequestParam(value = "deployId") String deployId,
@RequestParam(value = "procInsId", required = false) String procInsId) {
return R.ok(processService.selectFormContent(definitionId, deployId, procInsId));
}
/**
* 根据流程定义id启动流程实例
*
* @param processDefId 流程定义id
* @param variables 变量集合,json对象
*/
@SaCheckPermission("workflow:process:start")
@PostMapping("/start/{processDefId}")
public R<Void> start(@PathVariable(value = "processDefId") String processDefId, @RequestBody Map<String, Object> variables) {
// 如果包含审批人信息,则处理审批人信息
processService.startProcessByDefId(processDefId, variables);
return R.ok("流程启动成功");
}
/**
* 删除流程实例
*
* @param instanceIds 流程实例ID串
*/
@DeleteMapping("/instance/{instanceIds}")
public R<Void> delete(@PathVariable String[] instanceIds) {
processService.deleteProcessByIds(instanceIds);
return R.ok();
}
/**
* 读取xml文件
* @param processDefId 流程定义ID
*/
@GetMapping("/bpmnXml/{processDefId}")
public R<String> getBpmnXml(@PathVariable(value = "processDefId") String processDefId) {
return R.ok(null, processService.queryBpmnXmlById(processDefId));
}
/**
* 查询流程详情信息
*
* @param procInsId 流程实例ID
* @param taskId 任务ID
*/
@GetMapping("/detail")
public R detail(String procInsId, String taskId) {
return R.ok(processService.queryProcessDetail(procInsId, taskId));
}
}

View File

@@ -1,189 +0,0 @@
package com.klp.web.controller.workflow;
import cn.dev33.satoken.annotation.SaCheckPermission;
import cn.hutool.core.util.ObjectUtil;
import com.klp.common.core.domain.R;
import com.klp.workflow.domain.bo.WfTaskBo;
import com.klp.workflow.service.IWfTaskService;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.web.bind.annotation.*;
import javax.imageio.ImageIO;
import javax.servlet.http.HttpServletResponse;
import java.awt.image.BufferedImage;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
/**
* 工作流任务管理
*
* @author KonBAI
* @createTime 2022/3/10 00:12
*/
@Slf4j
@RequiredArgsConstructor
@RestController
@RequestMapping("/workflow/task")
public class WfTaskController {
private final IWfTaskService flowTaskService;
/**
* 取消流程
*/
@PostMapping(value = "/stopProcess")
@SaCheckPermission("workflow:process:cancel")
public R stopProcess(@RequestBody WfTaskBo bo) {
flowTaskService.stopProcess(bo);
return R.ok();
}
/**
* 撤回流程
*/
@PostMapping(value = "/revokeProcess")
@SaCheckPermission("workflow:process:revoke")
public R revokeProcess(@RequestBody WfTaskBo bo) {
flowTaskService.revokeProcess(bo);
return R.ok();
}
/**
* 获取流程变量
* @param taskId 流程任务Id
*/
@GetMapping(value = "/processVariables/{taskId}")
@SaCheckPermission("workflow:process:query")
public R processVariables(@PathVariable(value = "taskId") String taskId) {
return R.ok(flowTaskService.getProcessVariables(taskId));
}
/**
* 审批任务
*/
@PostMapping(value = "/complete")
@SaCheckPermission("workflow:process:approval")
public R complete(@RequestBody WfTaskBo bo) {
flowTaskService.complete(bo);
return R.ok();
}
/**
* 拒绝任务
*/
@PostMapping(value = "/reject")
@SaCheckPermission("workflow:process:approval")
public R taskReject(@RequestBody WfTaskBo taskBo) {
flowTaskService.taskReject(taskBo);
return R.ok();
}
/**
* 退回任务
*/
@PostMapping(value = "/return")
@SaCheckPermission("workflow:process:approval")
public R taskReturn(@RequestBody WfTaskBo bo) {
flowTaskService.taskReturn(bo);
return R.ok();
}
/**
* 获取所有可回退的节点
*/
@PostMapping(value = "/returnList")
@SaCheckPermission("workflow:process:query")
public R findReturnTaskList(@RequestBody WfTaskBo bo) {
return R.ok(flowTaskService.findReturnTaskList(bo));
}
/**
* 删除任务
*/
@DeleteMapping(value = "/delete")
@SaCheckPermission("workflow:process:approval")
public R delete(@RequestBody WfTaskBo bo) {
flowTaskService.deleteTask(bo);
return R.ok();
}
/**
* 认领/签收任务
*/
@PostMapping(value = "/claim")
@SaCheckPermission("workflow:process:claim")
public R claim(@RequestBody WfTaskBo bo) {
flowTaskService.claim(bo);
return R.ok();
}
/**
* 取消认领/签收任务
*/
@PostMapping(value = "/unClaim")
@SaCheckPermission("workflow:process:claim")
public R unClaim(@RequestBody WfTaskBo bo) {
flowTaskService.unClaim(bo);
return R.ok();
}
/**
* 委派任务
*/
@PostMapping(value = "/delegate")
@SaCheckPermission("workflow:process:approval")
public R delegate(@RequestBody WfTaskBo bo) {
if (ObjectUtil.hasNull(bo.getTaskId(), bo.getUserId())) {
return R.fail("参数错误!");
}
flowTaskService.delegateTask(bo);
return R.ok();
}
/**
* 转办任务
*/
@PostMapping(value = "/transfer")
@SaCheckPermission("workflow:process:approval")
public R transfer(@RequestBody WfTaskBo bo) {
if (ObjectUtil.hasNull(bo.getTaskId(), bo.getUserId())) {
return R.fail("参数错误!");
}
flowTaskService.transferTask(bo);
return R.ok();
}
/**
* 生成流程图
*
* @param processId 任务ID
*/
@RequestMapping("/diagram/{processId}")
public void genProcessDiagram(HttpServletResponse response,
@PathVariable("processId") String processId) {
InputStream inputStream = flowTaskService.diagram(processId);
OutputStream os = null;
BufferedImage image = null;
try {
image = ImageIO.read(inputStream);
response.setContentType("image/png");
os = response.getOutputStream();
if (image != null) {
ImageIO.write(image, "png", os);
}
} catch (Exception e) {
e.printStackTrace();
} finally {
try {
if (os != null) {
os.flush();
os.close();
}
} catch (IOException e) {
e.printStackTrace();
}
}
}
}

View File

@@ -298,19 +298,6 @@ management:
logfile:
external-file: ./logs/sys-console.log
--- # Flowable 配置
flowable:
# 关闭定时任务 job
async-executor-activate: false
# 库与数据库表结构不一致时,会自动将数据库表结构升级至新版本。
database-schema-update: true
idm:
# 关闭idm引擎 数据库不会创建act_id_*表流程流转不会使用act_id_*相关的表
enabled: false
# 关闭流程定义文件自动检查
check-process-definitions: false
# 关闭历史任务定时任务job
async-history-executor-activate: false
# 图像识别 OCR 配置(语言包绝对路径)
tesseract:

View File

@@ -1,37 +0,0 @@
<?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>klp-oa</artifactId>
<groupId>com.klp</groupId>
<version>0.8.3</version>
</parent>
<modelVersion>4.0.0</modelVersion>
<artifactId>klp-flowable</artifactId>
<description>
flowable工作流
</description>
<dependencies>
<dependency>
<groupId>com.klp</groupId>
<artifactId>klp-common</artifactId>
</dependency>
<dependency>
<groupId>org.flowable</groupId>
<artifactId>flowable-spring-boot-starter-process</artifactId>
</dependency>
<dependency>
<groupId>com.googlecode.aviator</groupId>
<artifactId>aviator</artifactId>
<version>5.3.1</version>
</dependency>
</dependencies>
</project>

View File

@@ -1,82 +0,0 @@
package com.klp.flowable.common.constant;
/**
* 流程常量信息
*
* @author Xuan xuan
* @date 2021/4/17 22:46
*/
public class ProcessConstants {
public static final String SUFFIX = ".bpmn";
/**
* 动态数据
*/
public static final String DATA_TYPE = "dynamic";
/**
* 单个审批人
*/
public static final String USER_TYPE_ASSIGNEE = "assignee";
/**
* 候选人
*/
public static final String USER_TYPE_USERS = "candidateUsers";
/**
* 审批组
*/
public static final String USER_TYPE_ROUPS = "candidateGroups";
/**
* 单个审批人
*/
public static final String PROCESS_APPROVAL = "approval";
/**
* 会签人员
*/
public static final String PROCESS_MULTI_INSTANCE_USER = "userList";
/**
* nameapace
*/
public static final String NAMASPASE = "http://flowable.org/bpmn";
/**
* 会签节点
*/
public static final String PROCESS_MULTI_INSTANCE = "multiInstance";
/**
* 自定义属性 dataType
*/
public static final String PROCESS_CUSTOM_DATA_TYPE = "dataType";
/**
* 自定义属性 userType
*/
public static final String PROCESS_CUSTOM_USER_TYPE = "userType";
/**
* 自定义属性 localScope
*/
public static final String PROCESS_FORM_LOCAL_SCOPE = "localScope";
/**
* 自定义属性 流程状态
*/
public static final String PROCESS_STATUS_KEY = "processStatus";
/**
* 流程跳过
*/
public static final String FLOWABLE_SKIP_EXPRESSION_ENABLED = "_FLOWABLE_SKIP_EXPRESSION_ENABLED";
}

View File

@@ -1,23 +0,0 @@
package com.klp.flowable.common.constant;
/**
* @author konbai
* @createTime 2022/4/24 13:24
*/
public class TaskConstants {
/**
* 流程发起人
*/
public static final String PROCESS_INITIATOR = "initiator";
/**
* 角色候选组前缀
*/
public static final String ROLE_GROUP_PREFIX = "ROLE";
/**
* 部门候选组前缀
*/
public static final String DEPT_GROUP_PREFIX = "DEPT";
}

View File

@@ -1,44 +0,0 @@
package com.klp.flowable.common.enums;
/**
* 流程意见类型
*
* @author Xuan xuan
* @date 2021/4/19
*/
public enum FlowComment {
/**
* 说明
*/
NORMAL("1", "正常"),
REBACK("2", "退回"),
REJECT("3", "驳回"),
DELEGATE("4", "委派"),
TRANSFER("5", "转办"),
STOP("6", "终止"),
REVOKE("7", "撤回");
/**
* 类型
*/
private final String type;
/**
* 说明
*/
private final String remark;
FlowComment(String type, String remark) {
this.type = type;
this.remark = remark;
}
public String getType() {
return type;
}
public String getRemark() {
return remark;
}
}

View File

@@ -1,33 +0,0 @@
package com.klp.flowable.common.enums;
import lombok.AllArgsConstructor;
import lombok.Getter;
/**
* @author KonBAI
* @createTime 2022/6/28 9:51
*/
@Getter
@AllArgsConstructor
public enum FormType {
/**
* 流程表单
*/
PROCESS(0),
/**
* 外置表单
*/
EXTERNAL(1),
/**
* 节点独立表单
*/
INDEPENDENT(2);
/**
* 表单类型
*/
private final Integer type;
}

View File

@@ -1,44 +0,0 @@
package com.klp.flowable.common.enums;
import com.klp.common.utils.StringUtils;
import lombok.AllArgsConstructor;
import lombok.Getter;
/**
* @author konbai
* @since 2023/3/9 00:45
*/
@Getter
@AllArgsConstructor
public enum ProcessStatus {
/**
* 进行中(审批中)
*/
RUNNING("running"),
/**
* 已终止
*/
TERMINATED("terminated"),
/**
* 已完成
*/
COMPLETED("completed"),
/**
* 已取消
*/
CANCELED("canceled");
private final String status;
public static ProcessStatus getProcessStatus(String str) {
if (StringUtils.isNotBlank(str)) {
for (ProcessStatus value : values()) {
if (StringUtils.equalsIgnoreCase(str, value.getStatus())) {
return value;
}
}
}
return null;
}
}

View File

@@ -1,28 +0,0 @@
package com.klp.flowable.config;
import com.klp.flowable.listener.GlobalEventListener;
import lombok.AllArgsConstructor;
import org.flowable.common.engine.api.delegate.event.FlowableEngineEventType;
import org.flowable.engine.RuntimeService;
import org.springframework.context.ApplicationListener;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.event.ContextRefreshedEvent;
/**
* flowable全局监听配置
*
* @author ssc
*/
@Configuration
@AllArgsConstructor
public class GlobalEventListenerConfig implements ApplicationListener<ContextRefreshedEvent> {
private final GlobalEventListener globalEventListener;
private final RuntimeService runtimeService;
@Override
public void onApplicationEvent(ContextRefreshedEvent event) {
// 流程正常结束
runtimeService.addEventListener(globalEventListener, FlowableEngineEventType.PROCESS_COMPLETED);
}
}

View File

@@ -1,94 +0,0 @@
package com.klp.flowable.config;
import org.flowable.bpmn.model.AssociationDirection;
import org.flowable.image.impl.DefaultProcessDiagramCanvas;
import java.awt.*;
import java.awt.geom.Line2D;
import java.awt.geom.RoundRectangle2D;
/**
* @author XuanXuan
* @date 2021-04-03
*/
public class MyDefaultProcessDiagramCanvas extends DefaultProcessDiagramCanvas {
//设置高亮线的颜色 这里我设置成绿色
protected static Color HIGHLIGHT_SEQUENCEFLOW_COLOR = Color.GREEN;
public MyDefaultProcessDiagramCanvas(int width, int height, int minX, int minY, String imageType, String activityFontName, String labelFontName, String annotationFontName, ClassLoader customClassLoader) {
super(width, height, minX, minY, imageType, activityFontName, labelFontName, annotationFontName, customClassLoader);
}
public MyDefaultProcessDiagramCanvas(int width, int height, int minX, int minY, String imageType) {
super(width, height, minX, minY, imageType);
}
/**
* 画线颜色设置
*/
@Override
public void drawConnection(int[] xPoints, int[] yPoints, boolean conditional, boolean isDefault, String connectionType,
AssociationDirection associationDirection, boolean highLighted, double scaleFactor) {
Paint originalPaint = g.getPaint();
Stroke originalStroke = g.getStroke();
g.setPaint(CONNECTION_COLOR);
if (connectionType.equals("association")) {
g.setStroke(ASSOCIATION_STROKE);
} else if (highLighted) {
//设置线的颜色
g.setPaint(originalPaint);
g.setStroke(HIGHLIGHT_FLOW_STROKE);
}
for (int i = 1; i < xPoints.length; i++) {
Integer sourceX = xPoints[i - 1];
Integer sourceY = yPoints[i - 1];
Integer targetX = xPoints[i];
Integer targetY = yPoints[i];
Line2D.Double line = new Line2D.Double(sourceX, sourceY, targetX, targetY);
g.draw(line);
}
if (isDefault) {
Line2D.Double line = new Line2D.Double(xPoints[0], yPoints[0], xPoints[1], yPoints[1]);
drawDefaultSequenceFlowIndicator(line, scaleFactor);
}
if (conditional) {
Line2D.Double line = new Line2D.Double(xPoints[0], yPoints[0], xPoints[1], yPoints[1]);
drawConditionalSequenceFlowIndicator(line, scaleFactor);
}
if (associationDirection == AssociationDirection.ONE || associationDirection == AssociationDirection.BOTH) {
Line2D.Double line = new Line2D.Double(xPoints[xPoints.length - 2], yPoints[xPoints.length - 2], xPoints[xPoints.length - 1], yPoints[xPoints.length - 1]);
drawArrowHead(line, scaleFactor);
}
if (associationDirection == AssociationDirection.BOTH) {
Line2D.Double line = new Line2D.Double(xPoints[1], yPoints[1], xPoints[0], yPoints[0]);
drawArrowHead(line, scaleFactor);
}
g.setPaint(originalPaint);
g.setStroke(originalStroke);
}
/**
* 高亮节点设置
*/
@Override
public void drawHighLight(int x, int y, int width, int height) {
Paint originalPaint = g.getPaint();
Stroke originalStroke = g.getStroke();
//设置高亮节点的颜色
g.setPaint(HIGHLIGHT_COLOR);
g.setStroke(THICK_TASK_BORDER_STROKE);
RoundRectangle2D rect = new RoundRectangle2D.Double(x, y, width, height, 20, 20);
g.draw(rect);
g.setPaint(originalPaint);
g.setStroke(originalStroke);
}
}

View File

@@ -1,65 +0,0 @@
package com.klp.flowable.core;
import lombok.Data;
import java.util.List;
import java.util.Map;
/**
* 表单属性类
*
* @author KonBAI
* @createTime 2022/8/6 18:54
*/
@Data
public class FormConf {
/**
* 标题
*/
private String title;
/**
* 表单名
*/
private String formRef;
/**
* 表单模型
*/
private String formModel;
/**
* 表单尺寸
*/
private String size;
/**
* 标签对齐
*/
private String labelPosition;
/**
* 标签宽度
*/
private Integer labelWidth;
/**
* 校验模型
*/
private String formRules;
/**
* 栅格间隔
*/
private Integer gutter;
/**
* 禁用表单
*/
private Boolean disabled = false;
/**
* 栅格占据的列数
*/
private Integer span;
/**
* 表单按钮
*/
private Boolean formBtns = true;
/**
* 表单项
*/
private List<Map<String, Object>> fields;
}

View File

@@ -1,41 +0,0 @@
package com.klp.flowable.core.domain;
import lombok.Data;
import java.util.HashMap;
import java.util.Map;
/**
* 流程查询实体对象
*
* @author KonBAI
* @createTime 2022/6/11 01:15
*/
@Data
public class ProcessQuery {
/**
* 流程标识
*/
private String processKey;
/**
* 流程名称
*/
private String processName;
/**
* 流程分类
*/
private String category;
/**
* 状态
*/
private String state;
/**
* 请求参数
*/
private Map<String, Object> params = new HashMap<>();
}

View File

@@ -1,44 +0,0 @@
package com.klp.flowable.factory;
import lombok.Getter;
import org.flowable.engine.*;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.stereotype.Component;
import javax.annotation.Resource;
/**
* flowable 引擎注入封装
* @author XuanXuan
* @date 2021-04-03
*/
@Component
@Getter
public class FlowServiceFactory {
@Resource
protected RepositoryService repositoryService;
@Resource
protected RuntimeService runtimeService;
@Resource
protected IdentityService identityService;
@Resource
protected TaskService taskService;
@Resource
protected FormService formService;
@Resource
protected HistoryService historyService;
@Resource
protected ManagementService managementService;
@Qualifier("processEngine")
@Resource
protected ProcessEngine processEngine;
}

View File

@@ -1,370 +0,0 @@
package com.klp.flowable.flow;
import org.flowable.bpmn.model.AssociationDirection;
import org.flowable.bpmn.model.GraphicInfo;
import org.flowable.image.impl.DefaultProcessDiagramCanvas;
import org.flowable.image.util.ReflectUtil;
import javax.imageio.ImageIO;
import java.awt.*;
import java.awt.font.FontRenderContext;
import java.awt.font.LineBreakMeasurer;
import java.awt.font.TextAttribute;
import java.awt.font.TextLayout;
import java.awt.geom.Ellipse2D;
import java.awt.geom.Rectangle2D;
import java.awt.geom.RoundRectangle2D;
import java.awt.image.BufferedImage;
import java.io.IOException;
import java.text.AttributedCharacterIterator;
import java.text.AttributedString;
/**
* @author XuanXuan
* @date 2021/4/4 23:58
*/
public class CustomProcessDiagramCanvas extends DefaultProcessDiagramCanvas {
//定义走过流程连线颜色为绿色
protected static Color HIGHLIGHT_SequenceFlow_COLOR = Color.GREEN;
//设置未走过流程的连接线颜色
protected static Color CONNECTION_COLOR = Color.BLACK;
//设置flows连接线字体颜色red
protected static Color LABEL_COLOR = new Color(0, 0, 0);
//高亮显示task框颜色
protected static Color HIGHLIGHT_COLOR = Color.GREEN;
protected static Color HIGHLIGHT_COLOR1 = Color.RED;
public CustomProcessDiagramCanvas(int width, int height, int minX, int minY, String imageType, String activityFontName, String labelFontName, String annotationFontName, ClassLoader customClassLoader) {
super(width, height, minX, minY, imageType, activityFontName, labelFontName, annotationFontName, customClassLoader);
this.initialize(imageType);
}
/**
* 重写绘制连线的方式,设置绘制颜色
* @param xPoints
* @param yPoints
* @param conditional
* @param isDefault
* @param connectionType
* @param associationDirection
* @param highLighted
* @param scaleFactor
*/
@Override
public void drawConnection(int[] xPoints, int[] yPoints, boolean conditional, boolean isDefault, String connectionType, AssociationDirection associationDirection, boolean highLighted, double scaleFactor) {
Paint originalPaint = this.g.getPaint();
Stroke originalStroke = this.g.getStroke();
this.g.setPaint(CONNECTION_COLOR);
if (connectionType.equals("association")) {
this.g.setStroke(ASSOCIATION_STROKE);
} else if (highLighted) {
this.g.setPaint(HIGHLIGHT_SequenceFlow_COLOR);
this.g.setStroke(HIGHLIGHT_FLOW_STROKE);
}
for (int i = 1; i < xPoints.length; ++i) {
int sourceX = xPoints[i - 1];
int sourceY = yPoints[i - 1];
int targetX = xPoints[i];
int targetY = yPoints[i];
java.awt.geom.Line2D.Double line = new java.awt.geom.Line2D.Double((double) sourceX, (double) sourceY, (double) targetX, (double) targetY);
this.g.draw(line);
}
java.awt.geom.Line2D.Double line;
if (isDefault) {
line = new java.awt.geom.Line2D.Double((double) xPoints[0], (double) yPoints[0], (double) xPoints[1], (double) yPoints[1]);
this.drawDefaultSequenceFlowIndicator(line, scaleFactor);
}
if (conditional) {
line = new java.awt.geom.Line2D.Double((double) xPoints[0], (double) yPoints[0], (double) xPoints[1], (double) yPoints[1]);
this.drawConditionalSequenceFlowIndicator(line, scaleFactor);
}
if (associationDirection.equals(AssociationDirection.ONE) || associationDirection.equals(AssociationDirection.BOTH)) {
line = new java.awt.geom.Line2D.Double((double) xPoints[xPoints.length - 2], (double) yPoints[xPoints.length - 2], (double) xPoints[xPoints.length - 1], (double) yPoints[xPoints.length - 1]);
this.drawArrowHead(line, scaleFactor);
}
if (associationDirection.equals(AssociationDirection.BOTH)) {
line = new java.awt.geom.Line2D.Double((double) xPoints[1], (double) yPoints[1], (double) xPoints[0], (double) yPoints[0]);
this.drawArrowHead(line, scaleFactor);
}
this.g.setPaint(originalPaint);
this.g.setStroke(originalStroke);
}
/**
* 设置字体大小图标颜色
* @param imageType
*/
@Override
public void initialize(String imageType) {
if ("png".equalsIgnoreCase(imageType)) {
this.processDiagram = new BufferedImage(this.canvasWidth, this.canvasHeight, 2);
} else {
this.processDiagram = new BufferedImage(this.canvasWidth, this.canvasHeight, 1);
}
this.g = this.processDiagram.createGraphics();
if (!"png".equalsIgnoreCase(imageType)) {
this.g.setBackground(new Color(255, 255, 255, 0));
this.g.clearRect(0, 0, this.canvasWidth, this.canvasHeight);
}
this.g.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
//修改图标颜色,修改图标字体大小
this.g.setPaint(Color.black);
Font font = new Font(this.activityFontName, 10, 14);
this.g.setFont(font);
this.fontMetrics = this.g.getFontMetrics();
//修改连接线字体大小
LABEL_FONT = new Font(this.labelFontName, 10, 15);
ANNOTATION_FONT = new Font(this.annotationFontName, 0, 11);
try {
USERTASK_IMAGE = ImageIO.read(ReflectUtil.getResource("org/flowable/icons/userTask.png", this.customClassLoader));
SCRIPTTASK_IMAGE = ImageIO.read(ReflectUtil.getResource("org/flowable/icons/scriptTask.png", this.customClassLoader));
SERVICETASK_IMAGE = ImageIO.read(ReflectUtil.getResource("org/flowable/icons/serviceTask.png", this.customClassLoader));
RECEIVETASK_IMAGE = ImageIO.read(ReflectUtil.getResource("org/flowable/icons/receiveTask.png", this.customClassLoader));
SENDTASK_IMAGE = ImageIO.read(ReflectUtil.getResource("org/flowable/icons/sendTask.png", this.customClassLoader));
MANUALTASK_IMAGE = ImageIO.read(ReflectUtil.getResource("org/flowable/icons/manualTask.png", this.customClassLoader));
BUSINESS_RULE_TASK_IMAGE = ImageIO.read(ReflectUtil.getResource("org/flowable/icons/businessRuleTask.png", this.customClassLoader));
SHELL_TASK_IMAGE = ImageIO.read(ReflectUtil.getResource("org/flowable/icons/shellTask.png", this.customClassLoader));
DMN_TASK_IMAGE = ImageIO.read(ReflectUtil.getResource("org/flowable/icons/dmnTask.png", this.customClassLoader));
CAMEL_TASK_IMAGE = ImageIO.read(ReflectUtil.getResource("org/flowable/icons/camelTask.png", this.customClassLoader));
MULE_TASK_IMAGE = ImageIO.read(ReflectUtil.getResource("org/flowable/icons/muleTask.png", this.customClassLoader));
HTTP_TASK_IMAGE = ImageIO.read(ReflectUtil.getResource("org/flowable/icons/httpTask.png", this.customClassLoader));
TIMER_IMAGE = ImageIO.read(ReflectUtil.getResource("org/flowable/icons/timer.png", this.customClassLoader));
COMPENSATE_THROW_IMAGE = ImageIO.read(ReflectUtil.getResource("org/flowable/icons/compensate-throw.png", this.customClassLoader));
COMPENSATE_CATCH_IMAGE = ImageIO.read(ReflectUtil.getResource("org/flowable/icons/compensate.png", this.customClassLoader));
ERROR_THROW_IMAGE = ImageIO.read(ReflectUtil.getResource("org/flowable/icons/error-throw.png", this.customClassLoader));
ERROR_CATCH_IMAGE = ImageIO.read(ReflectUtil.getResource("org/flowable/icons/error.png", this.customClassLoader));
MESSAGE_THROW_IMAGE = ImageIO.read(ReflectUtil.getResource("org/flowable/icons/message-throw.png", this.customClassLoader));
MESSAGE_CATCH_IMAGE = ImageIO.read(ReflectUtil.getResource("org/flowable/icons/message.png", this.customClassLoader));
SIGNAL_THROW_IMAGE = ImageIO.read(ReflectUtil.getResource("org/flowable/icons/signal-throw.png", this.customClassLoader));
SIGNAL_CATCH_IMAGE = ImageIO.read(ReflectUtil.getResource("org/flowable/icons/signal.png", this.customClassLoader));
} catch (IOException var4) {
LOGGER.warn("Could not load image for process diagram creation: {}", var4.getMessage());
}
}
/**
* 设置连接线字体
* @param text
* @param graphicInfo
* @param centered
*/
@Override
public void drawLabel(String text, GraphicInfo graphicInfo, boolean centered) {
float interline = 1.0f;
// text
if (text != null && text.length() > 0) {
Paint originalPaint = g.getPaint();
Font originalFont = g.getFont();
g.setPaint(LABEL_COLOR);
g.setFont(LABEL_FONT);
int wrapWidth = 100;
int textY = (int) graphicInfo.getY();
// TODO: use drawMultilineText()
AttributedString as = new AttributedString(text);
as.addAttribute(TextAttribute.FOREGROUND, g.getPaint());
as.addAttribute(TextAttribute.FONT, g.getFont());
AttributedCharacterIterator aci = as.getIterator();
FontRenderContext frc = new FontRenderContext(null, true, false);
LineBreakMeasurer lbm = new LineBreakMeasurer(aci, frc);
while (lbm.getPosition() < text.length()) {
TextLayout tl = lbm.nextLayout(wrapWidth);
textY += tl.getAscent();
Rectangle2D bb = tl.getBounds();
double tX = graphicInfo.getX();
if (centered) {
tX += (int) (graphicInfo.getWidth() / 2 - bb.getWidth() / 2);
}
tl.draw(g, (float) tX, textY);
textY += tl.getDescent() + tl.getLeading() + (interline - 1.0f) * tl.getAscent();
}
// restore originals
g.setFont(originalFont);
g.setPaint(originalPaint);
}
}
/**
* 高亮显示task框完成的
* @param x
* @param y
* @param width
* @param height
*/
@Override
public void drawHighLight(int x, int y, int width, int height) {
Paint originalPaint = g.getPaint();
Stroke originalStroke = g.getStroke();
g.setPaint(HIGHLIGHT_COLOR);
g.setStroke(THICK_TASK_BORDER_STROKE);
RoundRectangle2D rect = new RoundRectangle2D.Double(x, y, width, height, 20, 20);
g.draw(rect);
g.setPaint(originalPaint);
g.setStroke(originalStroke);
}
/**
* 自定义task框当前的位置
* @param x
* @param y
* @param width
* @param height
*/
public void drawHighLightNow(int x, int y, int width, int height) {
Paint originalPaint = g.getPaint();
Stroke originalStroke = g.getStroke();
g.setPaint(HIGHLIGHT_COLOR1);
g.setStroke(THICK_TASK_BORDER_STROKE);
RoundRectangle2D rect = new RoundRectangle2D.Double(x, y, width, height, 20, 20);
g.draw(rect);
g.setPaint(originalPaint);
g.setStroke(originalStroke);
}
/**
* 自定义结束节点
* @param x
* @param y
* @param width
* @param height
*/
public void drawHighLightEnd(int x, int y, int width, int height) {
Paint originalPaint = g.getPaint();
Stroke originalStroke = g.getStroke();
g.setPaint(HIGHLIGHT_COLOR);
g.setStroke(THICK_TASK_BORDER_STROKE);
RoundRectangle2D rect = new RoundRectangle2D.Double(x, y, width, height, 20, 20);
g.draw(rect);
g.setPaint(originalPaint);
g.setStroke(originalStroke);
}
/**
* task框自定义文字
* @param name
* @param graphicInfo
* @param thickBorder
* @param scaleFactor
*/
@Override
protected void drawTask(String name, GraphicInfo graphicInfo, boolean thickBorder, double scaleFactor) {
Paint originalPaint = g.getPaint();
int x = (int) graphicInfo.getX();
int y = (int) graphicInfo.getY();
int width = (int) graphicInfo.getWidth();
int height = (int) graphicInfo.getHeight();
// Create a new gradient paint for every task box, gradient depends on x and y and is not relative
g.setPaint(TASK_BOX_COLOR);
int arcR = 6;
if (thickBorder) {
arcR = 3;
}
// shape
RoundRectangle2D rect = new RoundRectangle2D.Double(x, y, width, height, arcR, arcR);
g.fill(rect);
g.setPaint(TASK_BORDER_COLOR);
if (thickBorder) {
Stroke originalStroke = g.getStroke();
g.setStroke(THICK_TASK_BORDER_STROKE);
g.draw(rect);
g.setStroke(originalStroke);
} else {
g.draw(rect);
}
g.setPaint(originalPaint);
// text
if (scaleFactor == 1.0 && name != null && name.length() > 0) {
int boxWidth = width - (2 * TEXT_PADDING);
int boxHeight = height - 16 - ICON_PADDING - ICON_PADDING - MARKER_WIDTH - 2 - 2;
int boxX = x + width / 2 - boxWidth / 2;
int boxY = y + height / 2 - boxHeight / 2 + ICON_PADDING + ICON_PADDING - 2 - 2;
drawMultilineCentredText(name, boxX, boxY, boxWidth, boxHeight);
}
}
protected static Color EVENT_COLOR = new Color(255, 255, 255);
/**
* 重写开始事件
* @param graphicInfo
* @param image
* @param scaleFactor
*/
@Override
public void drawStartEvent(GraphicInfo graphicInfo, BufferedImage image, double scaleFactor) {
Paint originalPaint = g.getPaint();
g.setPaint(EVENT_COLOR);
Ellipse2D circle = new Ellipse2D.Double(graphicInfo.getX(), graphicInfo.getY(),
graphicInfo.getWidth(), graphicInfo.getHeight());
g.fill(circle);
g.setPaint(EVENT_BORDER_COLOR);
g.draw(circle);
g.setPaint(originalPaint);
if (image != null) {
// calculate coordinates to center image
int imageX = (int) Math.round(graphicInfo.getX() + (graphicInfo.getWidth() / 2) - (image.getWidth() / (2 * scaleFactor)));
int imageY = (int) Math.round(graphicInfo.getY() + (graphicInfo.getHeight() / 2) - (image.getHeight() / (2 * scaleFactor)));
g.drawImage(image, imageX, imageY,
(int) (image.getWidth() / scaleFactor), (int) (image.getHeight() / scaleFactor), null);
}
}
/**
* 重写结束事件
* @param graphicInfo
* @param scaleFactor
*/
@Override
public void drawNoneEndEvent(GraphicInfo graphicInfo, double scaleFactor) {
Paint originalPaint = g.getPaint();
Stroke originalStroke = g.getStroke();
g.setPaint(EVENT_COLOR);
Ellipse2D circle = new Ellipse2D.Double(graphicInfo.getX(), graphicInfo.getY(),
graphicInfo.getWidth(), graphicInfo.getHeight());
g.fill(circle);
g.setPaint(EVENT_BORDER_COLOR);
// g.setPaint(HIGHLIGHT_COLOR);
if (scaleFactor == 1.0) {
g.setStroke(END_EVENT_STROKE);
} else {
g.setStroke(new BasicStroke(2.0f));
}
g.draw(circle);
g.setStroke(originalStroke);
g.setPaint(originalPaint);
}
}

View File

@@ -1,404 +0,0 @@
package com.klp.flowable.flow;
import org.flowable.bpmn.model.Process;
import org.flowable.bpmn.model.*;
import org.flowable.image.impl.DefaultProcessDiagramCanvas;
import org.flowable.image.impl.DefaultProcessDiagramGenerator;
import java.util.Iterator;
import java.util.List;
/**
* @author XuanXuan
* @date 2021/4/5 0:31
*/
public class CustomProcessDiagramGenerator extends DefaultProcessDiagramGenerator {
@Override
protected DefaultProcessDiagramCanvas generateProcessDiagram(BpmnModel bpmnModel, String imageType, List<String> highLightedActivities, List<String> highLightedFlows, String activityFontName, String labelFontName, String annotationFontName, ClassLoader customClassLoader, double scaleFactor, boolean drawSequenceFlowNameWithNoLabelDI) {
this.prepareBpmnModel(bpmnModel);
DefaultProcessDiagramCanvas processDiagramCanvas = initProcessDiagramCanvas(bpmnModel, imageType, activityFontName, labelFontName, annotationFontName, customClassLoader);
Iterator var13 = bpmnModel.getPools().iterator();
while (var13.hasNext()) {
Pool process = (Pool) var13.next();
GraphicInfo subProcesses = bpmnModel.getGraphicInfo(process.getId());
processDiagramCanvas.drawPoolOrLane(process.getName(), subProcesses, scaleFactor);
}
var13 = bpmnModel.getProcesses().iterator();
Process process1;
Iterator subProcesses1;
while (var13.hasNext()) {
process1 = (Process) var13.next();
subProcesses1 = process1.getLanes().iterator();
while (subProcesses1.hasNext()) {
Lane artifact = (Lane) subProcesses1.next();
GraphicInfo subProcess = bpmnModel.getGraphicInfo(artifact.getId());
processDiagramCanvas.drawPoolOrLane(artifact.getName(), subProcess, scaleFactor);
}
}
var13 = bpmnModel.getProcesses().iterator();
while (var13.hasNext()) {
process1 = (Process) var13.next();
subProcesses1 = process1.findFlowElementsOfType(FlowNode.class).iterator();
while (subProcesses1.hasNext()) {
FlowNode artifact1 = (FlowNode) subProcesses1.next();
if (!this.isPartOfCollapsedSubProcess(artifact1, bpmnModel)) {
this.drawActivity(processDiagramCanvas, bpmnModel, artifact1, highLightedActivities, highLightedFlows, scaleFactor, Boolean.valueOf(drawSequenceFlowNameWithNoLabelDI));
}
}
}
var13 = bpmnModel.getProcesses().iterator();
label75:
while (true) {
List subProcesses2;
do {
if (!var13.hasNext()) {
return processDiagramCanvas;
}
process1 = (Process) var13.next();
subProcesses1 = process1.getArtifacts().iterator();
while (subProcesses1.hasNext()) {
Artifact artifact2 = (Artifact) subProcesses1.next();
this.drawArtifact(processDiagramCanvas, bpmnModel, artifact2);
}
subProcesses2 = process1.findFlowElementsOfType(SubProcess.class, true);
} while (subProcesses2 == null);
Iterator artifact3 = subProcesses2.iterator();
while (true) {
GraphicInfo graphicInfo;
SubProcess subProcess1;
do {
do {
if (!artifact3.hasNext()) {
continue label75;
}
subProcess1 = (SubProcess) artifact3.next();
graphicInfo = bpmnModel.getGraphicInfo(subProcess1.getId());
} while (graphicInfo != null && graphicInfo.getExpanded() != null && !graphicInfo.getExpanded().booleanValue());
} while (this.isPartOfCollapsedSubProcess(subProcess1, bpmnModel));
Iterator var19 = subProcess1.getArtifacts().iterator();
while (var19.hasNext()) {
Artifact subProcessArtifact = (Artifact) var19.next();
this.drawArtifact(processDiagramCanvas, bpmnModel, subProcessArtifact);
}
}
}
}
protected static DefaultProcessDiagramCanvas initProcessDiagramCanvas(BpmnModel bpmnModel, String imageType, String activityFontName, String labelFontName, String annotationFontName, ClassLoader customClassLoader) {
double minX = 1.7976931348623157E308D;
double maxX = 0.0D;
double minY = 1.7976931348623157E308D;
double maxY = 0.0D;
GraphicInfo nrOfLanes;
for (Iterator flowNodes = bpmnModel.getPools().iterator(); flowNodes.hasNext(); maxY = nrOfLanes.getY() + nrOfLanes.getHeight()) {
Pool artifacts = (Pool) flowNodes.next();
nrOfLanes = bpmnModel.getGraphicInfo(artifacts.getId());
minX = nrOfLanes.getX();
maxX = nrOfLanes.getX() + nrOfLanes.getWidth();
minY = nrOfLanes.getY();
}
List var23 = gatherAllFlowNodes(bpmnModel);
Iterator var24 = var23.iterator();
label155:
while (var24.hasNext()) {
FlowNode var26 = (FlowNode) var24.next();
GraphicInfo artifact = bpmnModel.getGraphicInfo(var26.getId());
if (artifact.getX() + artifact.getWidth() > maxX) {
maxX = artifact.getX() + artifact.getWidth();
}
if (artifact.getX() < minX) {
minX = artifact.getX();
}
if (artifact.getY() + artifact.getHeight() > maxY) {
maxY = artifact.getY() + artifact.getHeight();
}
if (artifact.getY() < minY) {
minY = artifact.getY();
}
Iterator process = var26.getOutgoingFlows().iterator();
while (true) {
List l;
do {
if (!process.hasNext()) {
continue label155;
}
SequenceFlow graphicInfoList = (SequenceFlow) process.next();
l = bpmnModel.getFlowLocationGraphicInfo(graphicInfoList.getId());
} while (l == null);
Iterator graphicInfo = l.iterator();
while (graphicInfo.hasNext()) {
GraphicInfo graphicInfo1 = (GraphicInfo) graphicInfo.next();
if (graphicInfo1.getX() > maxX) {
maxX = graphicInfo1.getX();
}
if (graphicInfo1.getX() < minX) {
minX = graphicInfo1.getX();
}
if (graphicInfo1.getY() > maxY) {
maxY = graphicInfo1.getY();
}
if (graphicInfo1.getY() < minY) {
minY = graphicInfo1.getY();
}
}
}
}
List var25 = gatherAllArtifacts(bpmnModel);
Iterator var27 = var25.iterator();
GraphicInfo var37;
while (var27.hasNext()) {
Artifact var29 = (Artifact) var27.next();
GraphicInfo var31 = bpmnModel.getGraphicInfo(var29.getId());
if (var31 != null) {
if (var31.getX() + var31.getWidth() > maxX) {
maxX = var31.getX() + var31.getWidth();
}
if (var31.getX() < minX) {
minX = var31.getX();
}
if (var31.getY() + var31.getHeight() > maxY) {
maxY = var31.getY() + var31.getHeight();
}
if (var31.getY() < minY) {
minY = var31.getY();
}
}
List var33 = bpmnModel.getFlowLocationGraphicInfo(var29.getId());
if (var33 != null) {
Iterator var35 = var33.iterator();
while (var35.hasNext()) {
var37 = (GraphicInfo) var35.next();
if (var37.getX() > maxX) {
maxX = var37.getX();
}
if (var37.getX() < minX) {
minX = var37.getX();
}
if (var37.getY() > maxY) {
maxY = var37.getY();
}
if (var37.getY() < minY) {
minY = var37.getY();
}
}
}
}
int var28 = 0;
Iterator var30 = bpmnModel.getProcesses().iterator();
while (var30.hasNext()) {
Process var32 = (Process) var30.next();
Iterator var34 = var32.getLanes().iterator();
while (var34.hasNext()) {
Lane var36 = (Lane) var34.next();
++var28;
var37 = bpmnModel.getGraphicInfo(var36.getId());
if (var37.getX() + var37.getWidth() > maxX) {
maxX = var37.getX() + var37.getWidth();
}
if (var37.getX() < minX) {
minX = var37.getX();
}
if (var37.getY() + var37.getHeight() > maxY) {
maxY = var37.getY() + var37.getHeight();
}
if (var37.getY() < minY) {
minY = var37.getY();
}
}
}
if (var23.isEmpty() && bpmnModel.getPools().isEmpty() && var28 == 0) {
minX = 0.0D;
minY = 0.0D;
}
return new CustomProcessDiagramCanvas((int) maxX + 10, (int) maxY + 10, (int) minX, (int) minY, imageType, activityFontName, labelFontName, annotationFontName, customClassLoader);
}
private static void drawHighLight(DefaultProcessDiagramCanvas processDiagramCanvas, GraphicInfo graphicInfo) {
processDiagramCanvas.drawHighLight((int) graphicInfo.getX(), (int) graphicInfo.getY(), (int) graphicInfo.getWidth(), (int) graphicInfo.getHeight());
}
private static void drawHighLightNow(CustomProcessDiagramCanvas processDiagramCanvas, GraphicInfo graphicInfo) {
processDiagramCanvas.drawHighLightNow((int) graphicInfo.getX(), (int) graphicInfo.getY(), (int) graphicInfo.getWidth(), (int) graphicInfo.getHeight());
}
private static void drawHighLightEnd(CustomProcessDiagramCanvas processDiagramCanvas, GraphicInfo graphicInfo) {
processDiagramCanvas.drawHighLightEnd((int) graphicInfo.getX(), (int) graphicInfo.getY(), (int) graphicInfo.getWidth(), (int) graphicInfo.getHeight());
}
@Override
protected void drawActivity(DefaultProcessDiagramCanvas processDiagramCanvas, BpmnModel bpmnModel,
FlowNode flowNode, List<String> highLightedActivities, List<String> highLightedFlows, double scaleFactor, Boolean drawSequenceFlowNameWithNoLabelDI) {
ActivityDrawInstruction drawInstruction = activityDrawInstructions.get(flowNode.getClass());
if (drawInstruction != null) {
drawInstruction.draw(processDiagramCanvas, bpmnModel, flowNode);
// Gather info on the multi instance marker
boolean multiInstanceSequential = false;
boolean multiInstanceParallel = false;
boolean collapsed = false;
if (flowNode instanceof Activity) {
Activity activity = (Activity) flowNode;
MultiInstanceLoopCharacteristics multiInstanceLoopCharacteristics = activity.getLoopCharacteristics();
if (multiInstanceLoopCharacteristics != null) {
multiInstanceSequential = multiInstanceLoopCharacteristics.isSequential();
multiInstanceParallel = !multiInstanceSequential;
}
}
// Gather info on the collapsed marker
GraphicInfo graphicInfo = bpmnModel.getGraphicInfo(flowNode.getId());
if (flowNode instanceof SubProcess) {
collapsed = graphicInfo.getExpanded() != null && !graphicInfo.getExpanded();
} else if (flowNode instanceof CallActivity) {
collapsed = true;
}
if (scaleFactor == 1.0) {
// Actually draw the markers
processDiagramCanvas.drawActivityMarkers((int) graphicInfo.getX(), (int) graphicInfo.getY(), (int) graphicInfo.getWidth(), (int) graphicInfo.getHeight(),
multiInstanceSequential, multiInstanceParallel, collapsed);
}
// Draw highlighted activities
if (highLightedActivities.contains(flowNode.getId())) {
if (highLightedActivities.get(highLightedActivities.size() - 1).equals(flowNode.getId())
&& !"endenv".equals(flowNode.getId())) {
if ((flowNode.getId().contains("Event_"))) {
drawHighLightEnd((CustomProcessDiagramCanvas) processDiagramCanvas, bpmnModel.getGraphicInfo(flowNode.getId()));
} else {
drawHighLightNow((CustomProcessDiagramCanvas) processDiagramCanvas, bpmnModel.getGraphicInfo(flowNode.getId()));
}
} else {
drawHighLight(processDiagramCanvas, bpmnModel.getGraphicInfo(flowNode.getId()));
}
}
}
// Outgoing transitions of activity
for (SequenceFlow sequenceFlow : flowNode.getOutgoingFlows()) {
boolean highLighted = (highLightedFlows.contains(sequenceFlow.getId()));
String defaultFlow = null;
if (flowNode instanceof Activity) {
defaultFlow = ((Activity) flowNode).getDefaultFlow();
} else if (flowNode instanceof Gateway) {
defaultFlow = ((Gateway) flowNode).getDefaultFlow();
}
boolean isDefault = false;
if (defaultFlow != null && defaultFlow.equalsIgnoreCase(sequenceFlow.getId())) {
isDefault = true;
}
boolean drawConditionalIndicator = sequenceFlow.getConditionExpression() != null && !(flowNode instanceof Gateway);
String sourceRef = sequenceFlow.getSourceRef();
String targetRef = sequenceFlow.getTargetRef();
FlowElement sourceElement = bpmnModel.getFlowElement(sourceRef);
FlowElement targetElement = bpmnModel.getFlowElement(targetRef);
List<GraphicInfo> graphicInfoList = bpmnModel.getFlowLocationGraphicInfo(sequenceFlow.getId());
if (graphicInfoList != null && graphicInfoList.size() > 0) {
graphicInfoList = connectionPerfectionizer(processDiagramCanvas, bpmnModel, sourceElement, targetElement, graphicInfoList);
int xPoints[] = new int[graphicInfoList.size()];
int yPoints[] = new int[graphicInfoList.size()];
for (int i = 1; i < graphicInfoList.size(); i++) {
GraphicInfo graphicInfo = graphicInfoList.get(i);
GraphicInfo previousGraphicInfo = graphicInfoList.get(i - 1);
if (i == 1) {
xPoints[0] = (int) previousGraphicInfo.getX();
yPoints[0] = (int) previousGraphicInfo.getY();
}
xPoints[i] = (int) graphicInfo.getX();
yPoints[i] = (int) graphicInfo.getY();
}
processDiagramCanvas.drawSequenceflow(xPoints, yPoints, drawConditionalIndicator, isDefault, highLighted, scaleFactor);
// Draw sequenceflow label
GraphicInfo labelGraphicInfo = bpmnModel.getLabelGraphicInfo(sequenceFlow.getId());
if (labelGraphicInfo != null) {
processDiagramCanvas.drawLabel(sequenceFlow.getName(), labelGraphicInfo, false);
} else {
if (drawSequenceFlowNameWithNoLabelDI) {
GraphicInfo lineCenter = getLineCenter(graphicInfoList);
processDiagramCanvas.drawLabel(sequenceFlow.getName(), lineCenter, false);
}
}
}
}
// Nested elements
if (flowNode instanceof FlowElementsContainer) {
for (FlowElement nestedFlowElement : ((FlowElementsContainer) flowNode).getFlowElements()) {
if (nestedFlowElement instanceof FlowNode && !isPartOfCollapsedSubProcess(nestedFlowElement, bpmnModel)) {
drawActivity(processDiagramCanvas, bpmnModel, (FlowNode) nestedFlowElement,
highLightedActivities, highLightedFlows, scaleFactor, drawSequenceFlowNameWithNoLabelDI);
}
}
}
}
}

View File

@@ -1,221 +0,0 @@
package com.klp.flowable.flow;
import com.googlecode.aviator.AviatorEvaluator;
import com.googlecode.aviator.Expression;
import org.flowable.bpmn.model.Process;
import org.flowable.bpmn.model.*;
import org.flowable.engine.RepositoryService;
import org.flowable.engine.repository.ProcessDefinition;
import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
import java.util.Map;
/**
* @author Xuan xuan
* @date 2021/4/19 20:51
*/
public class FindNextNodeUtil {
/**
* 获取下一步骤的用户任务
*
* @param repositoryService
* @param map
* @return
*/
public static List<UserTask> getNextUserTasks(RepositoryService repositoryService, org.flowable.task.api.Task task, Map<String, Object> map) {
List<UserTask> data = new ArrayList<>();
ProcessDefinition processDefinition = repositoryService.createProcessDefinitionQuery().processDefinitionId(task.getProcessDefinitionId()).singleResult();
BpmnModel bpmnModel = repositoryService.getBpmnModel(processDefinition.getId());
Process mainProcess = bpmnModel.getMainProcess();
Collection<FlowElement> flowElements = mainProcess.getFlowElements();
String key = task.getTaskDefinitionKey();
FlowElement flowElement = bpmnModel.getFlowElement(key);
next(flowElements, flowElement, map, data);
return data;
}
public static void next(Collection<FlowElement> flowElements, FlowElement flowElement, Map<String, Object> map, List<UserTask> nextUser) {
//如果是结束节点
if (flowElement instanceof EndEvent) {
//如果是子任务的结束节点
if (getSubProcess(flowElements, flowElement) != null) {
flowElement = getSubProcess(flowElements, flowElement);
}
}
//获取Task的出线信息--可以拥有多个
List<SequenceFlow> outGoingFlows = null;
if (flowElement instanceof Task) {
outGoingFlows = ((Task) flowElement).getOutgoingFlows();
} else if (flowElement instanceof Gateway) {
outGoingFlows = ((Gateway) flowElement).getOutgoingFlows();
} else if (flowElement instanceof StartEvent) {
outGoingFlows = ((StartEvent) flowElement).getOutgoingFlows();
} else if (flowElement instanceof SubProcess) {
outGoingFlows = ((SubProcess) flowElement).getOutgoingFlows();
} else if (flowElement instanceof CallActivity) {
outGoingFlows = ((CallActivity) flowElement).getOutgoingFlows();
}
if (outGoingFlows != null && outGoingFlows.size() > 0) {
//遍历所有的出线--找到可以正确执行的那一条
for (SequenceFlow sequenceFlow : outGoingFlows) {
//1.有表达式且为true
//2.无表达式
String expression = sequenceFlow.getConditionExpression();
if (expression == null || expressionResult(map, expression.substring(expression.lastIndexOf("{") + 1, expression.lastIndexOf("}")))) {
//出线的下一节点
String nextFlowElementID = sequenceFlow.getTargetRef();
if (checkSubProcess(nextFlowElementID, flowElements, nextUser)) {
continue;
}
//查询下一节点的信息
FlowElement nextFlowElement = getFlowElementById(nextFlowElementID, flowElements);
//调用流程
if (nextFlowElement instanceof CallActivity) {
CallActivity ca = (CallActivity) nextFlowElement;
if (ca.getLoopCharacteristics() != null) {
UserTask userTask = new UserTask();
userTask.setId(ca.getId());
userTask.setId(ca.getId());
userTask.setLoopCharacteristics(ca.getLoopCharacteristics());
userTask.setName(ca.getName());
nextUser.add(userTask);
}
next(flowElements, nextFlowElement, map, nextUser);
}
//用户任务
if (nextFlowElement instanceof UserTask) {
nextUser.add((UserTask) nextFlowElement);
}
//排他网关
else if (nextFlowElement instanceof ExclusiveGateway) {
next(flowElements, nextFlowElement, map, nextUser);
}
//并行网关
else if (nextFlowElement instanceof ParallelGateway) {
next(flowElements, nextFlowElement, map, nextUser);
}
//接收任务
else if (nextFlowElement instanceof ReceiveTask) {
next(flowElements, nextFlowElement, map, nextUser);
}
//服务任务
else if (nextFlowElement instanceof ServiceTask) {
next(flowElements, nextFlowElement, map, nextUser);
}
//子任务的起点
else if (nextFlowElement instanceof StartEvent) {
next(flowElements, nextFlowElement, map, nextUser);
}
//结束节点
else if (nextFlowElement instanceof EndEvent) {
next(flowElements, nextFlowElement, map, nextUser);
}
}
}
}
}
/**
* 判断是否是多实例子流程并且需要设置集合类型变量
*/
public static boolean checkSubProcess(String Id, Collection<FlowElement> flowElements, List<UserTask> nextUser) {
for (FlowElement flowElement1 : flowElements) {
if (flowElement1 instanceof SubProcess && flowElement1.getId().equals(Id)) {
SubProcess sp = (SubProcess) flowElement1;
if (sp.getLoopCharacteristics() != null) {
String inputDataItem = sp.getLoopCharacteristics().getInputDataItem();
UserTask userTask = new UserTask();
userTask.setId(sp.getId());
userTask.setLoopCharacteristics(sp.getLoopCharacteristics());
userTask.setName(sp.getName());
nextUser.add(userTask);
return true;
}
}
}
return false;
}
/**
* 查询一个节点的是否子任务中的节点,如果是,返回子任务
*
* @param flowElements 全流程的节点集合
* @param flowElement 当前节点
* @return
*/
public static FlowElement getSubProcess(Collection<FlowElement> flowElements, FlowElement flowElement) {
for (FlowElement flowElement1 : flowElements) {
if (flowElement1 instanceof SubProcess) {
for (FlowElement flowElement2 : ((SubProcess) flowElement1).getFlowElements()) {
if (flowElement.equals(flowElement2)) {
return flowElement1;
}
}
}
}
return null;
}
/**
* 根据ID查询流程节点对象, 如果是子任务,则返回子任务的开始节点
*
* @param Id 节点ID
* @param flowElements 流程节点集合
* @return
*/
public static FlowElement getFlowElementById(String Id, Collection<FlowElement> flowElements) {
for (FlowElement flowElement : flowElements) {
if (flowElement.getId().equals(Id)) {
//如果是子任务,则查询出子任务的开始节点
if (flowElement instanceof SubProcess) {
return getStartFlowElement(((SubProcess) flowElement).getFlowElements());
}
return flowElement;
}
if (flowElement instanceof SubProcess) {
FlowElement flowElement1 = getFlowElementById(Id, ((SubProcess) flowElement).getFlowElements());
if (flowElement1 != null) {
return flowElement1;
}
}
}
return null;
}
/**
* 返回流程的开始节点
*
* @param flowElements 节点集合
* @description:
*/
public static FlowElement getStartFlowElement(Collection<FlowElement> flowElements) {
for (FlowElement flowElement : flowElements) {
if (flowElement instanceof StartEvent) {
return flowElement;
}
}
return null;
}
/**
* 校验el表达式
*
* @param map
* @param expression
* @return
*/
public static boolean expressionResult(Map<String, Object> map, String expression) {
Expression exp = AviatorEvaluator.compile(expression);
final Object execute = exp.execute(map);
return Boolean.parseBoolean(String.valueOf(execute));
}
}

View File

@@ -1,23 +0,0 @@
package com.klp.flowable.flow;
import org.flowable.spring.SpringProcessEngineConfiguration;
import org.flowable.spring.boot.EngineConfigurationConfigurer;
import org.springframework.context.annotation.Configuration;
/**
* @author XuanXuan
* @date 2021/4/5 01:32
*/
@Configuration
public class FlowableConfig implements EngineConfigurationConfigurer<SpringProcessEngineConfiguration> {
@Override
public void configure(SpringProcessEngineConfiguration engineConfiguration) {
engineConfiguration.setActivityFontName("宋体");
engineConfiguration.setLabelFontName("宋体");
engineConfiguration.setAnnotationFontName("宋体");
}
}

View File

@@ -1,704 +0,0 @@
package com.klp.flowable.flow;
import cn.hutool.core.util.ObjectUtil;
import com.klp.common.exception.ServiceException;
import lombok.extern.slf4j.Slf4j;
import org.flowable.bpmn.model.*;
import org.flowable.engine.impl.bpmn.behavior.ParallelMultiInstanceBehavior;
import org.flowable.engine.impl.bpmn.behavior.SequentialMultiInstanceBehavior;
import org.flowable.task.api.history.HistoricTaskInstance;
import java.util.*;
/**
* @author XuanXuan
* @date 2021-04-03 23:57
*/
@Slf4j
public class FlowableUtils {
/**
* 根据节点,获取入口连线
* @param source
* @return
*/
public static List<SequenceFlow> getElementIncomingFlows(FlowElement source) {
List<SequenceFlow> sequenceFlows = null;
if (source instanceof FlowNode) {
sequenceFlows = ((FlowNode) source).getIncomingFlows();
} else if (source instanceof Gateway) {
sequenceFlows = ((Gateway) source).getIncomingFlows();
} else if (source instanceof SubProcess) {
sequenceFlows = ((SubProcess) source).getIncomingFlows();
} else if (source instanceof StartEvent) {
sequenceFlows = ((StartEvent) source).getIncomingFlows();
} else if (source instanceof EndEvent) {
sequenceFlows = ((EndEvent) source).getIncomingFlows();
}
return sequenceFlows;
}
/**
* 根据节点,获取出口连线
* @param source
* @return
*/
public static List<SequenceFlow> getElementOutgoingFlows(FlowElement source) {
List<SequenceFlow> sequenceFlows = null;
if (source instanceof FlowNode) {
sequenceFlows = ((FlowNode) source).getOutgoingFlows();
} else if (source instanceof Gateway) {
sequenceFlows = ((Gateway) source).getOutgoingFlows();
} else if (source instanceof SubProcess) {
sequenceFlows = ((SubProcess) source).getOutgoingFlows();
} else if (source instanceof StartEvent) {
sequenceFlows = ((StartEvent) source).getOutgoingFlows();
} else if (source instanceof EndEvent) {
sequenceFlows = ((EndEvent) source).getOutgoingFlows();
}
return sequenceFlows;
}
/**
* 获取全部节点列表,包含子流程节点
* @param flowElements
* @param allElements
* @return
*/
public static Collection<FlowElement> getAllElements(Collection<FlowElement> flowElements, Collection<FlowElement> allElements) {
allElements = allElements == null ? new ArrayList<>() : allElements;
for (FlowElement flowElement : flowElements) {
allElements.add(flowElement);
if (flowElement instanceof SubProcess) {
// 继续深入子流程,进一步获取子流程
allElements = FlowableUtils.getAllElements(((SubProcess) flowElement).getFlowElements(), allElements);
}
}
return allElements;
}
/**
* 迭代获取父级任务节点列表,向前找
* @param source 起始节点
* @param hasSequenceFlow 已经经过的连线的 ID用于判断线路是否重复
* @param userTaskList 已找到的用户任务节点
* @return
*/
public static List<UserTask> iteratorFindParentUserTasks(FlowElement source, Set<String> hasSequenceFlow, List<UserTask> userTaskList) {
userTaskList = userTaskList == null ? new ArrayList<>() : userTaskList;
hasSequenceFlow = hasSequenceFlow == null ? new HashSet<>() : hasSequenceFlow;
// 如果该节点为开始节点,且存在上级子节点,则顺着上级子节点继续迭代
if (source instanceof StartEvent && source.getSubProcess() != null) {
userTaskList = iteratorFindParentUserTasks(source.getSubProcess(), hasSequenceFlow, userTaskList);
}
// 根据类型,获取入口连线
List<SequenceFlow> sequenceFlows = getElementIncomingFlows(source);
if (sequenceFlows != null) {
// 循环找到目标元素
for (SequenceFlow sequenceFlow: sequenceFlows) {
// 如果发现连线重复,说明循环了,跳过这个循环
if (hasSequenceFlow.contains(sequenceFlow.getId())) {
continue;
}
// 添加已经走过的连线
hasSequenceFlow.add(sequenceFlow.getId());
// 类型为用户节点,则新增父级节点
if (sequenceFlow.getSourceFlowElement() instanceof UserTask) {
userTaskList.add((UserTask) sequenceFlow.getSourceFlowElement());
continue;
}
// 类型为子流程,则添加子流程开始节点出口处相连的节点
if (sequenceFlow.getSourceFlowElement() instanceof SubProcess) {
// 获取子流程用户任务节点
List<UserTask> childUserTaskList = findChildProcessUserTasks((StartEvent) ((SubProcess) sequenceFlow.getSourceFlowElement()).getFlowElements().toArray()[0], null, null);
// 如果找到节点,则说明该线路找到节点,不继续向下找,反之继续
if (childUserTaskList != null && childUserTaskList.size() > 0) {
userTaskList.addAll(childUserTaskList);
continue;
}
}
// 继续迭代
userTaskList = iteratorFindParentUserTasks(sequenceFlow.getSourceFlowElement(), hasSequenceFlow, userTaskList);
}
}
return userTaskList;
}
/**
* 根据正在运行的任务节点,迭代获取子级任务节点列表,向后找
* @param source 起始节点
* @param runTaskKeyList 正在运行的任务 Key用于校验任务节点是否是正在运行的节点
* @param hasSequenceFlow 已经经过的连线的 ID用于判断线路是否重复
* @param userTaskList 需要撤回的用户任务列表
* @return
*/
public static List<UserTask> iteratorFindChildUserTasks(FlowElement source, List<String> runTaskKeyList, Set<String> hasSequenceFlow, List<UserTask> userTaskList) {
hasSequenceFlow = hasSequenceFlow == null ? new HashSet<>() : hasSequenceFlow;
userTaskList = userTaskList == null ? new ArrayList<>() : userTaskList;
// 如果该节点为开始节点,且存在上级子节点,则顺着上级子节点继续迭代
if (source instanceof StartEvent && source.getSubProcess() != null) {
userTaskList = iteratorFindChildUserTasks(source.getSubProcess(), runTaskKeyList, hasSequenceFlow, userTaskList);
}
// 根据类型,获取出口连线
List<SequenceFlow> sequenceFlows = getElementOutgoingFlows(source);
if (sequenceFlows != null) {
// 循环找到目标元素
for (SequenceFlow sequenceFlow: sequenceFlows) {
// 如果发现连线重复,说明循环了,跳过这个循环
if (hasSequenceFlow.contains(sequenceFlow.getId())) {
continue;
}
// 添加已经走过的连线
hasSequenceFlow.add(sequenceFlow.getId());
// 如果为用户任务类型,且任务节点的 Key 正在运行的任务中存在,添加
if (sequenceFlow.getTargetFlowElement() instanceof UserTask && runTaskKeyList.contains((sequenceFlow.getTargetFlowElement()).getId())) {
userTaskList.add((UserTask) sequenceFlow.getTargetFlowElement());
continue;
}
// 如果节点为子流程节点情况,则从节点中的第一个节点开始获取
if (sequenceFlow.getTargetFlowElement() instanceof SubProcess) {
List<UserTask> childUserTaskList = iteratorFindChildUserTasks((FlowElement) (((SubProcess) sequenceFlow.getTargetFlowElement()).getFlowElements().toArray()[0]), runTaskKeyList, hasSequenceFlow, null);
// 如果找到节点,则说明该线路找到节点,不继续向下找,反之继续
if (childUserTaskList != null && childUserTaskList.size() > 0) {
userTaskList.addAll(childUserTaskList);
continue;
}
}
// 继续迭代
userTaskList = iteratorFindChildUserTasks(sequenceFlow.getTargetFlowElement(), runTaskKeyList, hasSequenceFlow, userTaskList);
}
}
return userTaskList;
}
/**
* 迭代获取子流程用户任务节点
* @param source 起始节点
* @param hasSequenceFlow 已经经过的连线的 ID用于判断线路是否重复
* @param userTaskList 需要撤回的用户任务列表
* @return
*/
public static List<UserTask> findChildProcessUserTasks(FlowElement source, Set<String> hasSequenceFlow, List<UserTask> userTaskList) {
hasSequenceFlow = hasSequenceFlow == null ? new HashSet<>() : hasSequenceFlow;
userTaskList = userTaskList == null ? new ArrayList<>() : userTaskList;
// 根据类型,获取出口连线
List<SequenceFlow> sequenceFlows = getElementOutgoingFlows(source);
if (sequenceFlows != null) {
// 循环找到目标元素
for (SequenceFlow sequenceFlow: sequenceFlows) {
// 如果发现连线重复,说明循环了,跳过这个循环
if (hasSequenceFlow.contains(sequenceFlow.getId())) {
continue;
}
// 添加已经走过的连线
hasSequenceFlow.add(sequenceFlow.getId());
// 如果为用户任务类型,且任务节点的 Key 正在运行的任务中存在,添加
if (sequenceFlow.getTargetFlowElement() instanceof UserTask) {
userTaskList.add((UserTask) sequenceFlow.getTargetFlowElement());
continue;
}
// 如果节点为子流程节点情况,则从节点中的第一个节点开始获取
if (sequenceFlow.getTargetFlowElement() instanceof SubProcess) {
List<UserTask> childUserTaskList = findChildProcessUserTasks((FlowElement) (((SubProcess) sequenceFlow.getTargetFlowElement()).getFlowElements().toArray()[0]), hasSequenceFlow, null);
// 如果找到节点,则说明该线路找到节点,不继续向下找,反之继续
if (childUserTaskList != null && childUserTaskList.size() > 0) {
userTaskList.addAll(childUserTaskList);
continue;
}
}
// 继续迭代
userTaskList = findChildProcessUserTasks(sequenceFlow.getTargetFlowElement(), hasSequenceFlow, userTaskList);
}
}
return userTaskList;
}
/**
* 从后向前寻路,获取所有脏线路上的点
* @param source 起始节点
* @param passRoads 已经经过的点集合
* @param hasSequenceFlow 已经经过的连线的 ID用于判断线路是否重复
* @param targets 目标脏线路终点
* @param dirtyRoads 确定为脏数据的点,因为不需要重复,因此使用 set 存储
* @return
*/
public static Set<String> iteratorFindDirtyRoads(FlowElement source, List<String> passRoads, Set<String> hasSequenceFlow, List<String> targets, Set<String> dirtyRoads) {
passRoads = passRoads == null ? new ArrayList<>() : passRoads;
dirtyRoads = dirtyRoads == null ? new HashSet<>() : dirtyRoads;
hasSequenceFlow = hasSequenceFlow == null ? new HashSet<>() : hasSequenceFlow;
// 如果该节点为开始节点,且存在上级子节点,则顺着上级子节点继续迭代
if (source instanceof StartEvent && source.getSubProcess() != null) {
dirtyRoads = iteratorFindDirtyRoads(source.getSubProcess(), passRoads, hasSequenceFlow, targets, dirtyRoads);
}
// 根据类型,获取入口连线
List<SequenceFlow> sequenceFlows = getElementIncomingFlows(source);
if (sequenceFlows != null) {
// 循环找到目标元素
for (SequenceFlow sequenceFlow: sequenceFlows) {
// 如果发现连线重复,说明循环了,跳过这个循环
if (hasSequenceFlow.contains(sequenceFlow.getId())) {
continue;
}
// 添加已经走过的连线
hasSequenceFlow.add(sequenceFlow.getId());
// 新增经过的路线
passRoads.add(sequenceFlow.getSourceFlowElement().getId());
// 如果此点为目标点,确定经过的路线为脏线路,添加点到脏线路中,然后找下个连线
if (targets.contains(sequenceFlow.getSourceFlowElement().getId())) {
dirtyRoads.addAll(passRoads);
continue;
}
// 如果该节点为开始节点,且存在上级子节点,则顺着上级子节点继续迭代
if (sequenceFlow.getSourceFlowElement() instanceof SubProcess) {
dirtyRoads = findChildProcessAllDirtyRoad((StartEvent) ((SubProcess) sequenceFlow.getSourceFlowElement()).getFlowElements().toArray()[0], null, dirtyRoads);
// 是否存在子流程上true 是false 否
Boolean isInChildProcess = dirtyTargetInChildProcess((StartEvent) ((SubProcess) sequenceFlow.getSourceFlowElement()).getFlowElements().toArray()[0], null, targets, null);
if (isInChildProcess) {
// 已在子流程上找到,该路线结束
continue;
}
}
// 继续迭代
dirtyRoads = iteratorFindDirtyRoads(sequenceFlow.getSourceFlowElement(), passRoads, hasSequenceFlow, targets, dirtyRoads);
}
}
return dirtyRoads;
}
/**
* 迭代获取子流程脏路线
* 说明,假如回退的点就是子流程,那么也肯定会回退到子流程最初的用户任务节点,因此子流程中的节点全是脏路线
* @param source 起始节点
* @param hasSequenceFlow 已经经过的连线的 ID用于判断线路是否重复
* @param dirtyRoads 确定为脏数据的点,因为不需要重复,因此使用 set 存储
* @return
*/
public static Set<String> findChildProcessAllDirtyRoad(FlowElement source, Set<String> hasSequenceFlow, Set<String> dirtyRoads) {
hasSequenceFlow = hasSequenceFlow == null ? new HashSet<>() : hasSequenceFlow;
dirtyRoads = dirtyRoads == null ? new HashSet<>() : dirtyRoads;
// 根据类型,获取出口连线
List<SequenceFlow> sequenceFlows = getElementOutgoingFlows(source);
if (sequenceFlows != null) {
// 循环找到目标元素
for (SequenceFlow sequenceFlow: sequenceFlows) {
// 如果发现连线重复,说明循环了,跳过这个循环
if (hasSequenceFlow.contains(sequenceFlow.getId())) {
continue;
}
// 添加已经走过的连线
hasSequenceFlow.add(sequenceFlow.getId());
// 添加脏路线
dirtyRoads.add(sequenceFlow.getTargetFlowElement().getId());
// 如果节点为子流程节点情况,则从节点中的第一个节点开始获取
if (sequenceFlow.getTargetFlowElement() instanceof SubProcess) {
dirtyRoads = findChildProcessAllDirtyRoad((FlowElement) (((SubProcess) sequenceFlow.getTargetFlowElement()).getFlowElements().toArray()[0]), hasSequenceFlow, dirtyRoads);
}
// 继续迭代
dirtyRoads = findChildProcessAllDirtyRoad(sequenceFlow.getTargetFlowElement(), hasSequenceFlow, dirtyRoads);
}
}
return dirtyRoads;
}
/**
* 判断脏路线结束节点是否在子流程上
* @param source 起始节点
* @param hasSequenceFlow 已经经过的连线的 ID用于判断线路是否重复
* @param targets 判断脏路线节点是否存在子流程上,只要存在一个,说明脏路线只到子流程为止
* @param inChildProcess 是否存在子流程上true 是false 否
* @return
*/
public static Boolean dirtyTargetInChildProcess(FlowElement source, Set<String> hasSequenceFlow, List<String> targets, Boolean inChildProcess) {
hasSequenceFlow = hasSequenceFlow == null ? new HashSet<>() : hasSequenceFlow;
inChildProcess = inChildProcess == null ? false : inChildProcess;
// 根据类型,获取出口连线
List<SequenceFlow> sequenceFlows = getElementOutgoingFlows(source);
if (sequenceFlows != null && !inChildProcess) {
// 循环找到目标元素
for (SequenceFlow sequenceFlow: sequenceFlows) {
// 如果发现连线重复,说明循环了,跳过这个循环
if (hasSequenceFlow.contains(sequenceFlow.getId())) {
continue;
}
// 添加已经走过的连线
hasSequenceFlow.add(sequenceFlow.getId());
// 如果发现目标点在子流程上存在,说明只到子流程为止
if (targets.contains(sequenceFlow.getTargetFlowElement().getId())) {
inChildProcess = true;
break;
}
// 如果节点为子流程节点情况,则从节点中的第一个节点开始获取
if (sequenceFlow.getTargetFlowElement() instanceof SubProcess) {
inChildProcess = dirtyTargetInChildProcess((FlowElement) (((SubProcess) sequenceFlow.getTargetFlowElement()).getFlowElements().toArray()[0]), hasSequenceFlow, targets, inChildProcess);
}
// 继续迭代
inChildProcess = dirtyTargetInChildProcess(sequenceFlow.getTargetFlowElement(), hasSequenceFlow, targets, inChildProcess);
}
}
return inChildProcess;
}
/**
* 迭代从后向前扫描,判断目标节点相对于当前节点是否是串行
* 不存在直接回退到子流程中的情况,但存在从子流程出去到父流程情况
* @param source 起始节点
* @param isSequential 是否串行
* @param hasSequenceFlow 已经经过的连线的 ID用于判断线路是否重复
* @param targetKsy 目标节点
* @return
*/
public static Boolean iteratorCheckSequentialReferTarget(FlowElement source, String targetKsy, Set<String> hasSequenceFlow, Boolean isSequential) {
isSequential = isSequential == null ? true : isSequential;
hasSequenceFlow = hasSequenceFlow == null ? new HashSet<>() : hasSequenceFlow;
// 如果该节点为开始节点,且存在上级子节点,则顺着上级子节点继续迭代
if (source instanceof StartEvent && source.getSubProcess() != null) {
isSequential = iteratorCheckSequentialReferTarget(source.getSubProcess(), targetKsy, hasSequenceFlow, isSequential);
}
// 根据类型,获取入口连线
List<SequenceFlow> sequenceFlows = getElementIncomingFlows(source);
if (sequenceFlows != null) {
// 循环找到目标元素
for (SequenceFlow sequenceFlow: sequenceFlows) {
// 如果发现连线重复,说明循环了,跳过这个循环
if (hasSequenceFlow.contains(sequenceFlow.getId())) {
continue;
}
// 添加已经走过的连线
hasSequenceFlow.add(sequenceFlow.getId());
// 如果目标节点已被判断为并行,后面都不需要执行,直接返回
if (isSequential == false) {
break;
}
// 这条线路存在目标节点,这条线路完成,进入下个线路
if (targetKsy.equals(sequenceFlow.getSourceFlowElement().getId())) {
continue;
}
if (sequenceFlow.getSourceFlowElement() instanceof StartEvent) {
isSequential = false;
break;
}
// 否则就继续迭代
isSequential = iteratorCheckSequentialReferTarget(sequenceFlow.getSourceFlowElement(), targetKsy, hasSequenceFlow, isSequential);
}
}
return isSequential;
}
/**
* 从后向前寻路,获取到达节点的所有路线
* 不存在直接回退到子流程,但是存在回退到父级流程的情况
* @param source 起始节点
* @param passRoads 已经经过的点集合
* @param roads 路线
* @return
*/
public static List<List<UserTask>> findRoad(FlowElement source, List<UserTask> passRoads, Set<String> hasSequenceFlow, List<List<UserTask>> roads) {
passRoads = passRoads == null ? new ArrayList<>() : passRoads;
roads = roads == null ? new ArrayList<>() : roads;
hasSequenceFlow = hasSequenceFlow == null ? new HashSet<>() : hasSequenceFlow;
// 如果该节点为开始节点,且存在上级子节点,则顺着上级子节点继续迭代
if (source instanceof StartEvent && source.getSubProcess() != null) {
roads = findRoad(source.getSubProcess(), passRoads, hasSequenceFlow, roads);
}
// 根据类型,获取入口连线
List<SequenceFlow> sequenceFlows = getElementIncomingFlows(source);
if (sequenceFlows != null && sequenceFlows.size() != 0) {
for (SequenceFlow sequenceFlow: sequenceFlows) {
// 如果发现连线重复,说明循环了,跳过这个循环
if (hasSequenceFlow.contains(sequenceFlow.getId())) {
continue;
}
// 添加已经走过的连线
hasSequenceFlow.add(sequenceFlow.getId());
// 添加经过路线
if (sequenceFlow.getSourceFlowElement() instanceof UserTask) {
passRoads.add((UserTask) sequenceFlow.getSourceFlowElement());
}
// 继续迭代
roads = findRoad(sequenceFlow.getSourceFlowElement(), passRoads, hasSequenceFlow, roads);
}
} else {
// 添加路线
roads.add(passRoads);
}
return roads;
}
/**
* 历史节点数据清洗,清洗掉又回滚导致的脏数据
* @param allElements 全部节点信息
* @param historicTaskInstanceList 历史任务实例信息,数据采用开始时间升序
* @return
*/
public static List<String> historicTaskInstanceClean(Collection<FlowElement> allElements, List<HistoricTaskInstance> historicTaskInstanceList) {
// 会签节点收集
List<String> multiTask = new ArrayList<>();
allElements.forEach(flowElement -> {
if (flowElement instanceof UserTask) {
// 如果该节点的行为为会签行为,说明该节点为会签节点
if (((UserTask) flowElement).getBehavior() instanceof ParallelMultiInstanceBehavior || ((UserTask) flowElement).getBehavior() instanceof SequentialMultiInstanceBehavior) {
multiTask.add(flowElement.getId());
}
}
});
// 循环放入栈,栈 LIFO后进先出
Stack<HistoricTaskInstance> stack = new Stack<>();
historicTaskInstanceList.forEach(item -> stack.push(item));
// 清洗后的历史任务实例
List<String> lastHistoricTaskInstanceList = new ArrayList<>();
// 网关存在可能只走了部分分支情况,且还存在跳转废弃数据以及其他分支数据的干扰,因此需要对历史节点数据进行清洗
// 临时用户任务 key
StringBuilder userTaskKey = null;
// 临时被删掉的任务 key存在并行情况
List<String> deleteKeyList = new ArrayList<>();
// 临时脏数据线路
List<Set<String>> dirtyDataLineList = new ArrayList<>();
// 由某个点跳到会签点,此时出现多个会签实例对应 1 个跳转情况,需要把这些连续脏数据都找到
// 会签特殊处理下标
int multiIndex = -1;
// 会签特殊处理 key
StringBuilder multiKey = null;
// 会签特殊处理操作标识
boolean multiOpera = false;
while (!stack.empty()) {
// 从这里开始 userTaskKey 都还是上个栈的 key
// 是否是脏数据线路上的点
final boolean[] isDirtyData = {false};
for (Set<String> oldDirtyDataLine : dirtyDataLineList) {
if (oldDirtyDataLine.contains(stack.peek().getTaskDefinitionKey())) {
isDirtyData[0] = true;
}
}
// 删除原因不为空,说明从这条数据开始回跳或者回退的
// MI_END会签完成后其他未签到节点的删除原因不在处理范围内
if (stack.peek().getDeleteReason() != null && !stack.peek().getDeleteReason().equals("MI_END")) {
// 可以理解为脏线路起点
String dirtyPoint = "";
if (stack.peek().getDeleteReason().indexOf("Change activity to ") >= 0) {
dirtyPoint = stack.peek().getDeleteReason().replace("Change activity to ", "");
}
// 会签回退删除原因有点不同
if (stack.peek().getDeleteReason().indexOf("Change parent activity to ") >= 0) {
dirtyPoint = stack.peek().getDeleteReason().replace("Change parent activity to ", "");
}
FlowElement dirtyTask = null;
// 获取变更节点的对应的入口处连线
// 如果是网关并行回退情况,会变成两条脏数据路线,效果一样
for (FlowElement flowElement : allElements) {
if (flowElement.getId().equals(stack.peek().getTaskDefinitionKey())) {
dirtyTask = flowElement;
}
}
// 获取脏数据线路
Set<String> dirtyDataLine = FlowableUtils.iteratorFindDirtyRoads(dirtyTask, null, null, Arrays.asList(dirtyPoint.split(",")), null);
// 自己本身也是脏线路上的点,加进去
dirtyDataLine.add(stack.peek().getTaskDefinitionKey());
log.info(stack.peek().getTaskDefinitionKey() + "点脏路线集合:" + dirtyDataLine);
// 是全新的需要添加的脏线路
boolean isNewDirtyData = true;
for (int i = 0; i < dirtyDataLineList.size(); i++) {
// 如果发现他的上个节点在脏线路内,说明这个点可能是并行的节点,或者连续驳回
// 这时,都以之前的脏线路节点为标准,只需合并脏线路即可,也就是路线补全
if (dirtyDataLineList.get(i).contains(userTaskKey.toString())) {
isNewDirtyData = false;
dirtyDataLineList.get(i).addAll(dirtyDataLine);
}
}
// 已确定时全新的脏线路
if (isNewDirtyData) {
// deleteKey 单一路线驳回到并行,这种同时生成多个新实例记录情况,这时 deleteKey 其实是由多个值组成
// 按照逻辑,回退后立刻生成的实例记录就是回退的记录
// 至于驳回所生成的 Key直接从删除原因中获取因为存在驳回到并行的情况
deleteKeyList.add(dirtyPoint + ",");
dirtyDataLineList.add(dirtyDataLine);
}
// 添加后,现在这个点变成脏线路上的点了
isDirtyData[0] = true;
}
// 如果不是脏线路上的点,说明是有效数据,添加历史实例 Key
if (!isDirtyData[0]) {
lastHistoricTaskInstanceList.add(stack.peek().getTaskDefinitionKey());
}
// 校验脏线路是否结束
for (int i = 0; i < deleteKeyList.size(); i ++) {
// 如果发现脏数据属于会签,记录下下标与对应 Key以备后续比对会签脏数据范畴开始
if (multiKey == null && multiTask.contains(stack.peek().getTaskDefinitionKey())
&& deleteKeyList.get(i).contains(stack.peek().getTaskDefinitionKey())) {
multiIndex = i;
multiKey = new StringBuilder(stack.peek().getTaskDefinitionKey());
}
// 会签脏数据处理,节点退回会签清空
// 如果在会签脏数据范畴中发现 Key改变说明会签脏数据在上个节点就结束了可以把会签脏数据删掉
if (multiKey != null && !multiKey.toString().equals(stack.peek().getTaskDefinitionKey())) {
deleteKeyList.set(multiIndex , deleteKeyList.get(multiIndex).replace(stack.peek().getTaskDefinitionKey() + ",", ""));
multiKey = null;
// 结束进行下校验删除
multiOpera = true;
}
// 其他脏数据处理
// 发现该路线最后一条脏数据,说明这条脏数据线路处理完了,删除脏数据信息
// 脏数据产生的新实例中是否包含这条数据
if (multiKey == null && deleteKeyList.get(i).contains(stack.peek().getTaskDefinitionKey())) {
// 删除匹配到的部分
deleteKeyList.set(i , deleteKeyList.get(i).replace(stack.peek().getTaskDefinitionKey() + ",", ""));
}
// 如果每组中的元素都以匹配过,说明脏数据结束
if ("".equals(deleteKeyList.get(i))) {
// 同时删除脏数据
deleteKeyList.remove(i);
dirtyDataLineList.remove(i);
break;
}
}
// 会签数据处理需要在循环外处理,否则可能导致溢出
// 会签的数据肯定是之前放进去的所以理论上不会溢出,但还是校验下
if (multiOpera && deleteKeyList.size() > multiIndex && "".equals(deleteKeyList.get(multiIndex))) {
// 同时删除脏数据
deleteKeyList.remove(multiIndex);
dirtyDataLineList.remove(multiIndex);
multiIndex = -1;
multiOpera = false;
}
// pop() 方法与 peek() 方法不同,在返回值的同时,会把值从栈中移除
// 保存新的 userTaskKey 在下个循环中使用
userTaskKey = new StringBuilder(stack.pop().getTaskDefinitionKey());
}
log.info("清洗后的历史节点数据:" + lastHistoricTaskInstanceList);
return lastHistoricTaskInstanceList;
}
/**
* 深搜递归获取流程未通过的节点
* @param bpmnModel 流程模型
* @param unfinishedTaskSet 未结束的任务节点
* @param finishedSequenceFlowSet 已经完成的连线
* @param finishedTaskSet 已完成的任务节点
* @return
*/
public static Set<String> dfsFindRejects(BpmnModel bpmnModel, Set<String> unfinishedTaskSet, Set<String> finishedSequenceFlowSet, Set<String> finishedTaskSet) {
if (ObjectUtil.isNull(bpmnModel)) {
throw new ServiceException("流程模型不存在");
}
Collection<FlowElement> allElements = getAllElements(bpmnModel.getMainProcess().getFlowElements(), null);
Set<String> rejectedSet = new HashSet<>();
for (FlowElement flowElement : allElements) {
// 用户节点且未结束元素
if (flowElement instanceof UserTask && unfinishedTaskSet.contains(flowElement.getId())) {
List<String> hasSequenceFlow = iteratorFindFinishes(flowElement, null);
List<String> rejects = iteratorFindRejects(flowElement, finishedSequenceFlowSet, finishedTaskSet, hasSequenceFlow, null);
rejectedSet.addAll(rejects);
}
}
return rejectedSet;
}
/**
* 迭代获取父级节点列表,向前找
* @param source 起始节点
* @param hasSequenceFlow 已经经过的连线的ID用于判断线路是否重复
* @return
*/
public static List<String> iteratorFindFinishes(FlowElement source, List<String> hasSequenceFlow) {
hasSequenceFlow = hasSequenceFlow == null ? new ArrayList<>() : hasSequenceFlow;
// 根据类型,获取入口连线
List<SequenceFlow> sequenceFlows = getElementIncomingFlows(source);
if (sequenceFlows != null) {
// 循环找到目标元素
for (SequenceFlow sequenceFlow: sequenceFlows) {
// 如果发现连线重复,说明循环了,跳过这个循环
if (hasSequenceFlow.contains(sequenceFlow.getId())) {
continue;
}
// 添加已经走过的连线
hasSequenceFlow.add(sequenceFlow.getId());
FlowElement finishedElement = sequenceFlow.getSourceFlowElement();
// 类型为子流程,则添加子流程开始节点出口处相连的节点
if (finishedElement instanceof SubProcess) {
FlowElement firstElement = (StartEvent) ((SubProcess) finishedElement).getFlowElements().toArray()[0];
// 获取子流程的连线
hasSequenceFlow.addAll(iteratorFindFinishes(firstElement, null));
}
// 继续迭代
hasSequenceFlow = iteratorFindFinishes(finishedElement, hasSequenceFlow);
}
}
return hasSequenceFlow;
}
/**
* 根据正在运行的任务节点,迭代获取子级任务节点列表,向后找
* @param source 起始节点
* @param finishedSequenceFlowSet 已经完成的连线
* @param finishedTaskSet 已经完成的任务节点
* @param hasSequenceFlow 已经经过的连线的 ID用于判断线路是否重复
* @param rejectedList 未通过的元素
* @return
*/
public static List<String> iteratorFindRejects(FlowElement source, Set<String> finishedSequenceFlowSet, Set<String> finishedTaskSet
, List<String> hasSequenceFlow, List<String> rejectedList) {
hasSequenceFlow = hasSequenceFlow == null ? new ArrayList<>() : hasSequenceFlow;
rejectedList = rejectedList == null ? new ArrayList<>() : rejectedList;
// 根据类型,获取出口连线
List<SequenceFlow> sequenceFlows = getElementOutgoingFlows(source);
if (sequenceFlows != null) {
// 循环找到目标元素
for (SequenceFlow sequenceFlow: sequenceFlows) {
// 如果发现连线重复,说明循环了,跳过这个循环
if (hasSequenceFlow.contains(sequenceFlow.getId())) {
continue;
}
// 添加已经走过的连线
hasSequenceFlow.add(sequenceFlow.getId());
FlowElement targetElement = sequenceFlow.getTargetFlowElement();
// 添加未完成的节点
if (finishedTaskSet.contains(targetElement.getId())) {
rejectedList.add(targetElement.getId());
}
// 添加未完成的连线
if (finishedSequenceFlowSet.contains(sequenceFlow.getId())) {
rejectedList.add(sequenceFlow.getId());
}
// 如果节点为子流程节点情况,则从节点中的第一个节点开始获取
if (targetElement instanceof SubProcess) {
FlowElement firstElement = (FlowElement) (((SubProcess) targetElement).getFlowElements().toArray()[0]);
List<String> childList = iteratorFindRejects(firstElement, finishedSequenceFlowSet, finishedTaskSet, hasSequenceFlow, null);
// 如果找到节点,则说明该线路找到节点,不继续向下找,反之继续
if (childList != null && childList.size() > 0) {
rejectedList.addAll(childList);
continue;
}
}
// 继续迭代
rejectedList = iteratorFindRejects(targetElement, finishedSequenceFlowSet, finishedTaskSet, hasSequenceFlow, rejectedList);
}
}
return rejectedList;
}
}

View File

@@ -1,38 +0,0 @@
package com.klp.flowable.listener;
import cn.hutool.core.convert.Convert;
import cn.hutool.core.util.ObjectUtil;
import com.klp.flowable.common.constant.ProcessConstants;
import com.klp.flowable.common.enums.ProcessStatus;
import org.flowable.common.engine.api.delegate.event.FlowableEngineEntityEvent;
import org.flowable.engine.RuntimeService;
import org.flowable.engine.delegate.event.AbstractFlowableEngineEventListener;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
/**
* Flowable 全局监听器
*
* @author konbai
* @since 2023/3/8 22:45
*/
@Component
public class GlobalEventListener extends AbstractFlowableEngineEventListener {
@Autowired
private RuntimeService runtimeService;
/**
* 流程结束监听器
*/
@Override
protected void processCompleted(FlowableEngineEntityEvent event) {
String processInstanceId = event.getProcessInstanceId();
Object variable = runtimeService.getVariable(processInstanceId, ProcessConstants.PROCESS_STATUS_KEY);
ProcessStatus status = ProcessStatus.getProcessStatus(Convert.toStr(variable));
if (ObjectUtil.isNotNull(status) && ProcessStatus.RUNNING == status) {
runtimeService.setVariable(processInstanceId, ProcessConstants.PROCESS_STATUS_KEY, ProcessStatus.COMPLETED.getStatus());
}
super.processCompleted(event);
}
}

View File

@@ -1,28 +0,0 @@
package com.klp.flowable.listener;
import org.flowable.engine.delegate.TaskListener;
import org.flowable.task.service.delegate.DelegateTask;
import org.springframework.stereotype.Component;
/**
* 用户任务监听器
*
* @author KonBAI
* @since 2023/5/13
*/
@Component(value = "userTaskListener")
public class UserTaskListener implements TaskListener {
/**
* 注入字段(名称与流程设计时字段名称一致)
*/
// private FixedValue field;
@Override
public void notify(DelegateTask delegateTask) {
System.out.println("执行任务监听器...");
}
}

View File

@@ -1,373 +0,0 @@
package com.klp.flowable.utils;
import cn.hutool.core.util.ObjectUtil;
import cn.hutool.core.util.StrUtil;
import org.flowable.bpmn.converter.BpmnXMLConverter;
import org.flowable.bpmn.model.Process;
import org.flowable.bpmn.model.*;
import org.flowable.common.engine.impl.util.io.StringStreamSource;
import java.util.*;
/**
* @author KonBAI
* @createTime 2022/3/26 19:04
*/
public class ModelUtils {
private static final BpmnXMLConverter bpmnXMLConverter = new BpmnXMLConverter();
/**
* xml转bpmnModel对象
*
* @param xml xml
* @return bpmnModel对象
*/
public static BpmnModel getBpmnModel(String xml) {
return bpmnXMLConverter.convertToBpmnModel(new StringStreamSource(xml), false, false);
}
/**
* bpmnModel转xml字符串
*
* @deprecated 存在会丢失 bpmn 连线问题
* @param bpmnModel bpmnModel对象
* @return xml字符串
*/
@Deprecated
public static String getBpmnXmlStr(BpmnModel bpmnModel) {
return StrUtil.utf8Str(getBpmnXml(bpmnModel));
}
/**
* bpmnModel转xml对象
*
* @deprecated 存在丢失 bpmn 连线问题
* @param bpmnModel bpmnModel对象
* @return xml
*/
@Deprecated
public static byte[] getBpmnXml(BpmnModel bpmnModel) {
return bpmnXMLConverter.convertToXML(bpmnModel);
}
/**
* 根据节点,获取入口连线
*
* @param source 起始节点
* @return 入口连线列表
*/
public static List<SequenceFlow> getElementIncomingFlows(FlowElement source) {
List<SequenceFlow> sequenceFlows = new ArrayList<>();
if (source instanceof FlowNode) {
sequenceFlows = ((FlowNode) source).getIncomingFlows();
}
return sequenceFlows;
}
/**
* 根据节点,获取出口连线
*
* @param source 起始节点
* @return 出口连线列表
*/
public static List<SequenceFlow> getElementOutgoingFlows(FlowElement source) {
List<SequenceFlow> sequenceFlows = new ArrayList<>();
if (source instanceof FlowNode) {
sequenceFlows = ((FlowNode) source).getOutgoingFlows();
}
return sequenceFlows;
}
/**
* 获取开始节点
*
* @param model bpmnModel对象
* @return 开始节点未找到开始节点返回null
*/
public static StartEvent getStartEvent(BpmnModel model) {
Process process = model.getMainProcess();
FlowElement startElement = process.getInitialFlowElement();
if (startElement instanceof StartEvent) {
return (StartEvent) startElement;
}
return getStartEvent(process.getFlowElements());
}
/**
* 获取开始节点
*
* @param flowElements 流程元素集合
* @return 开始节点未找到开始节点返回null
*/
public static StartEvent getStartEvent(Collection<FlowElement> flowElements) {
for (FlowElement flowElement : flowElements) {
if (flowElement instanceof StartEvent) {
return (StartEvent) flowElement;
}
}
return null;
}
/**
* 获取结束节点
*
* @param model bpmnModel对象
* @return 结束节点未找到开始节点返回null
*/
public static EndEvent getEndEvent(BpmnModel model) {
Process process = model.getMainProcess();
return getEndEvent(process.getFlowElements());
}
/**
* 获取结束节点
*
* @param flowElements 流程元素集合
* @return 结束节点未找到开始节点返回null
*/
public static EndEvent getEndEvent(Collection<FlowElement> flowElements) {
for (FlowElement flowElement : flowElements) {
if (flowElement instanceof EndEvent) {
return (EndEvent) flowElement;
}
}
return null;
}
public static UserTask getUserTaskByKey(BpmnModel model, String taskKey) {
Process process = model.getMainProcess();
FlowElement flowElement = process.getFlowElement(taskKey);
if (flowElement instanceof UserTask) {
return (UserTask) flowElement;
}
return null;
}
/**
* 获取流程元素信息
*
* @param model bpmnModel对象
* @param flowElementId 元素ID
* @return 元素信息
*/
public static FlowElement getFlowElementById(BpmnModel model, String flowElementId) {
Process process = model.getMainProcess();
return process.getFlowElement(flowElementId);
}
/**
* 获取元素表单Key限开始节点和用户节点可用
*
* @param flowElement 元素
* @return 表单Key
*/
public static String getFormKey(FlowElement flowElement) {
if (flowElement != null) {
if (flowElement instanceof StartEvent) {
return ((StartEvent) flowElement).getFormKey();
} else if (flowElement instanceof UserTask) {
return ((UserTask) flowElement).getFormKey();
}
}
return null;
}
/**
* 获取开始节点属性值
* @param model bpmnModel对象
* @param name 属性名
* @return 属性值
*/
public static String getStartEventAttributeValue(BpmnModel model, String name) {
StartEvent startEvent = getStartEvent(model);
return getElementAttributeValue(startEvent, name);
}
/**
* 获取结束节点属性值
* @param model bpmnModel对象
* @param name 属性名
* @return 属性值
*/
public static String getEndEventAttributeValue(BpmnModel model, String name) {
EndEvent endEvent = getEndEvent(model);
return getElementAttributeValue(endEvent, name);
}
/**
* 获取用户任务节点属性值
* @param model bpmnModel对象
* @param taskKey 任务Key
* @param name 属性名
* @return 属性值
*/
public static String getUserTaskAttributeValue(BpmnModel model, String taskKey, String name) {
UserTask userTask = getUserTaskByKey(model, taskKey);
return getElementAttributeValue(userTask, name);
}
/**
* 获取元素属性值
* @param baseElement 流程元素
* @param name 属性名
* @return 属性值
*/
public static String getElementAttributeValue(BaseElement baseElement, String name) {
if (baseElement != null) {
List<ExtensionAttribute> attributes = baseElement.getAttributes().get(name);
if (attributes != null && !attributes.isEmpty()) {
attributes.iterator().next().getValue();
Iterator<ExtensionAttribute> attrIterator = attributes.iterator();
if(attrIterator.hasNext()) {
ExtensionAttribute attribute = attrIterator.next();
return attribute.getValue();
}
}
}
return null;
}
public static boolean isMultiInstance(BpmnModel model, String taskKey) {
UserTask userTask = getUserTaskByKey(model, taskKey);
if (ObjectUtil.isNotNull(userTask)) {
return userTask.hasMultiInstanceLoopCharacteristics();
}
return false;
}
/**
* 获取所有用户任务节点
*
* @param model bpmnModel对象
* @return 用户任务节点列表
*/
public static Collection<UserTask> getAllUserTaskEvent(BpmnModel model) {
Process process = model.getMainProcess();
Collection<FlowElement> flowElements = process.getFlowElements();
return getAllUserTaskEvent(flowElements, null);
}
/**
* 获取所有用户任务节点
* @param flowElements 流程元素集合
* @param allElements 所有流程元素集合
* @return 用户任务节点列表
*/
public static Collection<UserTask> getAllUserTaskEvent(Collection<FlowElement> flowElements, Collection<UserTask> allElements) {
allElements = allElements == null ? new ArrayList<>() : allElements;
for (FlowElement flowElement : flowElements) {
if (flowElement instanceof UserTask) {
allElements.add((UserTask) flowElement);
}
if (flowElement instanceof SubProcess) {
// 继续深入子流程,进一步获取子流程
allElements = getAllUserTaskEvent(((SubProcess) flowElement).getFlowElements(), allElements);
}
}
return allElements;
}
/**
* 查找起始节点下一个用户任务列表列表
* @param source 起始节点
* @return 结果
*/
public static List<UserTask> findNextUserTasks(FlowElement source) {
return findNextUserTasks(source, null, null);
}
/**
* 查找起始节点下一个用户任务列表列表
* @param source 起始节点
* @param hasSequenceFlow 已经经过的连线的 ID用于判断线路是否重复
* @param userTaskList 用户任务列表
* @return 结果
*/
public static List<UserTask> findNextUserTasks(FlowElement source, Set<String> hasSequenceFlow, List<UserTask> userTaskList) {
hasSequenceFlow = Optional.ofNullable(hasSequenceFlow).orElse(new HashSet<>());
userTaskList = Optional.ofNullable(userTaskList).orElse(new ArrayList<>());
// 获取出口连线
List<SequenceFlow> sequenceFlows = getElementOutgoingFlows(source);
if (!sequenceFlows.isEmpty()) {
for (SequenceFlow sequenceFlow : sequenceFlows) {
// 如果发现连线重复,说明循环了,跳过这个循环
if (hasSequenceFlow.contains(sequenceFlow.getId())) {
continue;
}
// 添加已经走过的连线
hasSequenceFlow.add(sequenceFlow.getId());
FlowElement targetFlowElement = sequenceFlow.getTargetFlowElement();
if (targetFlowElement instanceof UserTask) {
// 若节点为用户任务,加入到结果列表中
userTaskList.add((UserTask) targetFlowElement);
} else {
// 若节点非用户任务,继续递归查找下一个节点
findNextUserTasks(targetFlowElement, hasSequenceFlow, userTaskList);
}
}
}
return userTaskList;
}
/**
* 迭代从后向前扫描,判断目标节点相对于当前节点是否是串行
* 不存在直接回退到子流程中的情况,但存在从子流程出去到父流程情况
* @param source 起始节点
* @param target 目标节点
* @param visitedElements 已经经过的连线的 ID用于判断线路是否重复
* @return 结果
*/
public static boolean isSequentialReachable(FlowElement source, FlowElement target, Set<String> visitedElements) {
visitedElements = visitedElements == null ? new HashSet<>() : visitedElements;
if (source instanceof StartEvent && isInEventSubprocess(source)) {
return false;
}
// 根据类型,获取入口连线
List<SequenceFlow> sequenceFlows = getElementIncomingFlows(source);
if (sequenceFlows != null && sequenceFlows.size() > 0) {
// 循环找到目标元素
for (SequenceFlow sequenceFlow: sequenceFlows) {
// 如果发现连线重复,说明循环了,跳过这个循环
if (visitedElements.contains(sequenceFlow.getId())) {
continue;
}
// 添加已经走过的连线
visitedElements.add(sequenceFlow.getId());
FlowElement sourceFlowElement = sequenceFlow.getSourceFlowElement();
// 这条线路存在目标节点,这条线路完成,进入下个线路
if (target.getId().equals(sourceFlowElement.getId())) {
continue;
}
// 如果目标节点为并行网关,则不继续
if (sourceFlowElement instanceof ParallelGateway) {
return false;
}
// 否则就继续迭代
boolean isSequential = isSequentialReachable(sourceFlowElement, target, visitedElements);
if (!isSequential) {
return false;
}
}
}
return true;
}
protected static boolean isInEventSubprocess(FlowElement flowElement) {
FlowElementsContainer flowElementsContainer = flowElement.getParentContainer();
while (flowElementsContainer != null) {
if (flowElementsContainer instanceof EventSubProcess) {
return true;
}
if (flowElementsContainer instanceof FlowElement) {
flowElementsContainer = ((FlowElement) flowElementsContainer).getParentContainer();
} else {
flowElementsContainer = null;
}
}
return false;
}
}

View File

@@ -1,50 +0,0 @@
package com.klp.flowable.utils;
import cn.hutool.core.convert.Convert;
import com.klp.flowable.core.FormConf;
import java.util.List;
import java.util.Map;
/**
* 流程表单工具类
*
* @author KonBAI
* @createTime 2022/8/7 17:09
*/
public class ProcessFormUtils {
private static final String CONFIG = "__config__";
private static final String MODEL = "__vModel__";
/**
* 填充表单项内容
*
* @param formConf 表单配置信息
* @param data 表单内容
*/
public static void fillFormData(FormConf formConf, Map<String, Object> data) {
for (Map<String, Object> field : formConf.getFields()) {
recursiveFillField(field, data);
}
}
@SuppressWarnings("unchecked")
private static void recursiveFillField(final Map<String, Object> field, final Map<String, Object> data) {
if (!field.containsKey(CONFIG)) {
return;
}
Map<String, Object> configMap = (Map<String, Object>) field.get(CONFIG);
if (configMap.containsKey("children")) {
List<Map<String, Object>> childrens = (List<Map<String, Object>>) configMap.get("children");
for (Map<String, Object> children : childrens) {
recursiveFillField(children, data);
}
}
String modelKey = Convert.toStr(field.get(MODEL));
Object value = data.get(modelKey);
if (value != null) {
configMap.put("defaultValue", value);
}
}
}

View File

@@ -1,115 +0,0 @@
package com.klp.flowable.utils;
import com.klp.common.utils.DateUtils;
import com.klp.common.utils.StringUtils;
import com.klp.flowable.core.domain.ProcessQuery;
import org.flowable.common.engine.api.query.Query;
import org.flowable.common.engine.impl.db.SuspensionState;
import org.flowable.engine.history.HistoricProcessInstanceQuery;
import org.flowable.engine.repository.ProcessDefinitionQuery;
import org.flowable.task.api.TaskQuery;
import org.flowable.task.api.history.HistoricTaskInstanceQuery;
import java.util.Map;
/**
* 流程工具类
*
* @author konbai
* @since 2022/12/11 03:35
*/
public class ProcessUtils {
public static void buildProcessSearch(Query<?, ?> query, ProcessQuery process) {
if (query instanceof ProcessDefinitionQuery) {
buildProcessDefinitionSearch((ProcessDefinitionQuery) query, process);
} else if (query instanceof TaskQuery) {
buildTaskSearch((TaskQuery) query, process);
} else if (query instanceof HistoricTaskInstanceQuery) {
buildHistoricTaskInstanceSearch((HistoricTaskInstanceQuery) query, process);
} else if (query instanceof HistoricProcessInstanceQuery) {
buildHistoricProcessInstanceSearch((HistoricProcessInstanceQuery) query, process);
}
}
/**
* 构建流程定义搜索
*/
public static void buildProcessDefinitionSearch(ProcessDefinitionQuery query, ProcessQuery process) {
// 流程标识
if (StringUtils.isNotBlank(process.getProcessKey())) {
query.processDefinitionKeyLike("%" + process.getProcessKey() + "%");
}
// 流程名称
if (StringUtils.isNotBlank(process.getProcessName())) {
query.processDefinitionNameLike("%" + process.getProcessName() + "%");
}
// 流程分类
if (StringUtils.isNotBlank(process.getCategory())) {
query.processDefinitionCategory(process.getCategory());
}
// 流程状态
if (StringUtils.isNotBlank(process.getState())) {
if (SuspensionState.ACTIVE.toString().equals(process.getState())) {
query.active();
} else if (SuspensionState.SUSPENDED.toString().equals(process.getState())) {
query.suspended();
}
}
}
/**
* 构建任务搜索
*/
public static void buildTaskSearch(TaskQuery query, ProcessQuery process) {
Map<String, Object> params = process.getParams();
if (StringUtils.isNotBlank(process.getProcessKey())) {
query.processDefinitionKeyLike("%" + process.getProcessKey() + "%");
}
if (StringUtils.isNotBlank(process.getProcessName())) {
query.processDefinitionNameLike("%" + process.getProcessName() + "%");
}
if (params.get("beginTime") != null && params.get("endTime") != null) {
query.taskCreatedAfter(DateUtils.parseDate(params.get("beginTime")));
query.taskCreatedBefore(DateUtils.parseDate(params.get("endTime")));
}
}
private static void buildHistoricTaskInstanceSearch(HistoricTaskInstanceQuery query, ProcessQuery process) {
Map<String, Object> params = process.getParams();
if (StringUtils.isNotBlank(process.getProcessKey())) {
query.processDefinitionKeyLike("%" + process.getProcessKey() + "%");
}
if (StringUtils.isNotBlank(process.getProcessName())) {
query.processDefinitionNameLike("%" + process.getProcessName() + "%");
}
if (params.get("beginTime") != null && params.get("endTime") != null) {
query.taskCompletedAfter(DateUtils.parseDate(params.get("beginTime")));
query.taskCompletedBefore(DateUtils.parseDate(params.get("endTime")));
}
}
/**
* 构建历史流程实例搜索
*/
public static void buildHistoricProcessInstanceSearch(HistoricProcessInstanceQuery query, ProcessQuery process) {
Map<String, Object> params = process.getParams();
// 流程标识
if (StringUtils.isNotBlank(process.getProcessKey())) {
query.processDefinitionKey(process.getProcessKey());
}
// 流程名称
if (StringUtils.isNotBlank(process.getProcessName())) {
query.processDefinitionName(process.getProcessName());
}
// 流程名称
if (StringUtils.isNotBlank(process.getCategory())) {
query.processDefinitionCategory(process.getCategory());
}
if (params.get("beginTime") != null && params.get("endTime") != null) {
query.startedAfter(DateUtils.parseDate(params.get("beginTime")));
query.startedBefore(DateUtils.parseDate(params.get("endTime")));
}
}
}

View File

@@ -1,41 +0,0 @@
package com.klp.flowable.utils;
import cn.hutool.core.util.ObjectUtil;
import com.klp.common.core.domain.model.LoginUser;
import com.klp.common.helper.LoginHelper;
import com.klp.flowable.common.constant.TaskConstants;
import java.util.ArrayList;
import java.util.List;
/**
* 工作流任务工具类
*
* @author konbai
* @createTime 2022/4/24 12:42
*/
public class TaskUtils {
public static String getUserId() {
return String.valueOf(LoginHelper.getUserId());
}
/**
* 获取用户组信息
*
* @return candidateGroup
*/
public static List<String> getCandidateGroup() {
List<String> list = new ArrayList<>();
LoginUser user = LoginHelper.getLoginUser();
if (ObjectUtil.isNotNull(user)) {
if (ObjectUtil.isNotEmpty(user.getRoles())) {
user.getRoles().forEach(role -> list.add(TaskConstants.ROLE_GROUP_PREFIX + role.getRoleId()));
}
if (ObjectUtil.isNotNull(user.getDeptId())) {
list.add(TaskConstants.DEPT_GROUP_PREFIX + user.getDeptId());
}
}
return list;
}
}

View File

@@ -23,12 +23,6 @@
<artifactId>klp-common</artifactId>
</dependency>
<!-- Flowable功能模块 -->
<dependency>
<groupId>com.klp</groupId>
<artifactId>klp-flowable</artifactId>
</dependency>
<!-- OSS功能模块 -->
<dependency>
<groupId>com.klp</groupId>

View File

@@ -1,50 +0,0 @@
package com.klp.workflow.domain;
import com.baomidou.mybatisplus.annotation.TableId;
import com.baomidou.mybatisplus.annotation.TableLogic;
import com.baomidou.mybatisplus.annotation.TableName;
import com.klp.common.core.domain.BaseEntity;
import lombok.Data;
import lombok.EqualsAndHashCode;
import javax.validation.constraints.NotBlank;
/**
* 流程分类对象 wf_category
*
* @author KonBAI
* @date 2022-01-15
*/
@Data
@EqualsAndHashCode(callSuper = true)
@TableName("wf_category")
public class WfCategory extends BaseEntity {
private static final long serialVersionUID=1L;
/**
* 分类ID
*/
@TableId(value = "category_id")
private Long categoryId;
/**
* 分类名称
*/
@NotBlank(message = "分类名称不能为空")
private String categoryName;
/**
* 分类编码
*/
@NotBlank(message = "分类编码不能为空")
private String code;
/**
* 备注
*/
private String remark;
/**
* 删除标志0代表存在 2代表删除
*/
@TableLogic
private String delFlag;
}

View File

@@ -1,74 +0,0 @@
package com.klp.workflow.domain;
import com.baomidou.mybatisplus.annotation.TableId;
import com.baomidou.mybatisplus.annotation.TableLogic;
import com.baomidou.mybatisplus.annotation.TableName;
import com.klp.common.core.domain.BaseEntity;
import lombok.Data;
import lombok.EqualsAndHashCode;
/**
* 流程抄送对象 wf_copy
*
* @author KonBAI
* @date 2022-05-19
*/
@Data
@EqualsAndHashCode(callSuper = true)
@TableName("wf_copy")
public class WfCopy extends BaseEntity {
private static final long serialVersionUID=1L;
/**
* 抄送主键
*/
@TableId(value = "copy_id")
private Long copyId;
/**
* 抄送标题
*/
private String title;
/**
* 流程主键
*/
private String processId;
/**
* 流程名称
*/
private String processName;
/**
* 流程分类主键
*/
private String categoryId;
/**
* 部署主键
*/
private String deploymentId;
/**
* 流程实例主键
*/
private String instanceId;
/**
* 任务主键
*/
private String taskId;
/**
* 用户主键
*/
private Long userId;
/**
* 发起人Id
*/
private Long originatorId;
/**
* 发起人名称
*/
private String originatorName;
/**
* 删除标志0代表存在 2代表删除
*/
@TableLogic
private String delFlag;
}

View File

@@ -1,46 +0,0 @@
package com.klp.workflow.domain;
import com.baomidou.mybatisplus.annotation.TableName;
import lombok.Data;
/**
* 流程实例关联表单对象 sys_instance_form
*
* @author KonBAI
* @createTime 2022/3/7 22:07
*/
@Data
@TableName("wf_deploy_form")
public class WfDeployForm {
private static final long serialVersionUID = 1L;
/**
* 流程部署主键
*/
private String deployId;
/**
* 表单Key
*/
private String formKey;
/**
* 节点Key
*/
private String nodeKey;
/**
* 表单名称
*/
private String formName;
/**
* 节点名称
*/
private String nodeName;
/**
* 表单内容
*/
private String content;
}

View File

@@ -1,41 +0,0 @@
package com.klp.workflow.domain;
import com.baomidou.mybatisplus.annotation.TableId;
import com.baomidou.mybatisplus.annotation.TableName;
import com.klp.common.core.domain.BaseEntity;
import lombok.Data;
import lombok.EqualsAndHashCode;
/**
* 流程表单对象 wf_form
*
* @author KonBAI
* @createTime 2022/3/7 22:07
*/
@Data
@EqualsAndHashCode(callSuper = true)
@TableName("wf_form")
public class WfForm extends BaseEntity {
private static final long serialVersionUID = 1L;
/**
* 表单主键
*/
@TableId(value = "form_id")
private Long formId;
/**
* 表单名称
*/
private String formName;
/**
* 表单内容
*/
private String content;
/**
* 备注
*/
private String remark;
}

View File

@@ -1,75 +0,0 @@
package com.klp.workflow.domain.bo;
import com.klp.common.core.domain.BaseEntity;
import com.klp.common.core.validate.AddGroup;
import com.klp.common.core.validate.EditGroup;
import lombok.Data;
import lombok.EqualsAndHashCode;
import javax.validation.constraints.NotBlank;
import javax.validation.constraints.NotNull;
/**
* 流程抄送业务对象 wf_copy
*
* @author klp
* @date 2022-05-19
*/
@Data
@EqualsAndHashCode(callSuper = true)
public class WfCopyBo extends BaseEntity {
/**
* 抄送主键
*/
@NotNull(message = "抄送主键不能为空", groups = { EditGroup.class })
private Long copyId;
/**
* 抄送标题
*/
@NotNull(message = "抄送标题不能为空", groups = { AddGroup.class, EditGroup.class })
private String title;
/**
* 流程主键
*/
@NotBlank(message = "流程主键不能为空", groups = { AddGroup.class, EditGroup.class })
private String processId;
/**
* 流程名称
*/
@NotBlank(message = "流程名称不能为空", groups = { AddGroup.class, EditGroup.class })
private String processName;
/**
* 流程分类主键
*/
@NotBlank(message = "流程分类主键不能为空", groups = { AddGroup.class, EditGroup.class })
private String categoryId;
/**
* 任务主键
*/
@NotBlank(message = "任务主键不能为空", groups = { AddGroup.class, EditGroup.class })
private String taskId;
/**
* 用户主键
*/
@NotNull(message = "用户主键不能为空", groups = { AddGroup.class, EditGroup.class })
private Long userId;
/**
* 发起人Id
*/
@NotNull(message = "发起人主键不能为空", groups = { AddGroup.class, EditGroup.class })
private Long originatorId;
/**
* 发起人名称
*/
@NotNull(message = "发起人名称不能为空", groups = { AddGroup.class, EditGroup.class })
private String originatorName;
}

View File

@@ -1,36 +0,0 @@
package com.klp.workflow.domain.bo;
import com.klp.common.core.validate.AddGroup;
import com.klp.common.core.validate.EditGroup;
import lombok.Data;
import javax.validation.constraints.NotBlank;
import javax.validation.constraints.NotNull;
/**
* 流程设计业务对象
*
* @author KonBAI
* @createTime 2022/3/10 00:12
*/
@Data
public class WfDesignerBo {
/**
* 流程名称
*/
@NotNull(message = "流程名称", groups = { AddGroup.class, EditGroup.class })
private String name;
/**
* 流程分类
*/
@NotBlank(message = "流程分类", groups = { AddGroup.class, EditGroup.class })
private String category;
/**
* XML字符串
*/
@NotBlank(message = "XML字符串", groups = { AddGroup.class, EditGroup.class })
private String xml;
}

View File

@@ -1,45 +0,0 @@
package com.klp.workflow.domain.bo;
import com.klp.common.core.domain.BaseEntity;
import com.klp.common.core.validate.AddGroup;
import com.klp.common.core.validate.EditGroup;
import lombok.Data;
import lombok.EqualsAndHashCode;
import javax.validation.constraints.NotBlank;
import javax.validation.constraints.NotNull;
/**
* 流程表单业务对象
*
* @author KonBAI
* @createTime 2022/3/7 22:07
*/
@Data
@EqualsAndHashCode(callSuper = true)
public class WfFormBo extends BaseEntity {
private static final long serialVersionUID = 1L;
/**
* 表单主键
*/
@NotNull(message = "表单ID不能为空", groups = { EditGroup.class })
private Long formId;
/**
* 表单名称
*/
@NotBlank(message = "表单名称不能为空", groups = { AddGroup.class, EditGroup.class })
private String formName;
/**
* 表单内容
*/
@NotBlank(message = "表单内容不能为空", groups = { AddGroup.class, EditGroup.class })
private String content;
/**
* 备注
*/
private String remark;
}

View File

@@ -1,58 +0,0 @@
package com.klp.workflow.domain.bo;
import com.klp.common.core.validate.AddGroup;
import com.klp.common.core.validate.EditGroup;
import lombok.Data;
import javax.validation.constraints.NotBlank;
import javax.validation.constraints.NotNull;
/**
* 流程模型对象
*
* @author KonBAI
* @createTime 2022/6/21 9:16
*/
@Data
public class WfModelBo {
/**
* 模型主键
*/
@NotNull(message = "模型主键不能为空", groups = { EditGroup.class })
private String modelId;
/**
* 模型名称
*/
@NotNull(message = "模型名称不能为空", groups = { AddGroup.class, EditGroup.class })
private String modelName;
/**
* 模型Key
*/
@NotNull(message = "模型Key不能为空", groups = { AddGroup.class, EditGroup.class })
private String modelKey;
/**
* 流程分类
*/
@NotBlank(message = "流程分类不能为空", groups = { AddGroup.class, EditGroup.class })
private String category;
/**
* 描述
*/
private String description;
/**
* 表单类型
*/
private Integer formType;
/**
* 表单主键
*/
private Long formId;
/**
* 流程xml
*/
private String bpmnXml;
/**
* 是否保存为新版本
*/
private Boolean newVersion;
}

View File

@@ -1,64 +0,0 @@
package com.klp.workflow.domain.bo;
import lombok.Data;
import java.util.List;
import java.util.Map;
/**
* 流程任务业务对象
*
* @author KonBAI
* @createTime 2022/3/10 00:12
*/
@Data
public class WfTaskBo {
/**
* 任务Id
*/
private String taskId;
/**
* 任务名称
*/
private String taskName;
/**
* 用户Id
*/
private String userId;
/**
* 任务意见
*/
private String comment;
/**
* 流程实例Id
*/
private String procInsId;
/**
* 节点
*/
private String targetKey;
/**
* 流程变量信息
*/
private Map<String, Object> variables;
/**
* 审批人
*/
private String assignee;
/**
* 候选人
*/
private List<String> candidateUsers;
/**
* 审批组
*/
private List<String> candidateGroups;
/**
* 抄送用户Id
*/
private String copyUserIds;
/**
* 下一节点审批人
*/
private String nextUserIds;
}

View File

@@ -1,25 +0,0 @@
package com.klp.workflow.domain.dto;
import lombok.Builder;
import lombok.Data;
import java.io.Serializable;
/**
* @author KonBAI
* @createTime 2022/3/10 00:12
*/
@Data
@Builder
public class WfCommentDto implements Serializable {
/**
* 意见类别 0 正常意见 1 退回意见 2 驳回意见
*/
private String type;
/**
* 意见内容
*/
private String comment;
}

View File

@@ -1,30 +0,0 @@
package com.klp.workflow.domain.dto;
import lombok.Data;
/**
* @author KonBAI
* @createTime 2022/6/21 9:16
*/
@Data
public class WfMetaInfoDto {
/**
* 创建者username
*/
private String createUser;
/**
* 流程描述
*/
private String description;
/**
* 表单类型
*/
private Integer formType;
/**
* 表单编号
*/
private Long formId;
}

View File

@@ -1,25 +0,0 @@
package com.klp.workflow.domain.dto;
import com.klp.common.core.domain.entity.SysRole;
import com.klp.common.core.domain.entity.SysUser;
import lombok.Data;
import java.io.Serializable;
import java.util.List;
/**
* 动态人员、组
* @author KonBAI
* @createTime 2022/3/10 00:12
*/
@Data
public class WfNextDto implements Serializable {
private String type;
private String vars;
private List<SysUser> userList;
private List<SysRole> roleList;
}

View File

@@ -1,46 +0,0 @@
package com.klp.workflow.domain.vo;
import com.alibaba.excel.annotation.ExcelIgnoreUnannotated;
import com.alibaba.excel.annotation.ExcelProperty;
import lombok.Data;
/**
* 流程分类视图对象 flow_category
*
* @author KonBAI
* @date 2022-01-15
*/
@Data
@ExcelIgnoreUnannotated
public class WfCategoryVo {
private static final long serialVersionUID = 1L;
/**
* 分类ID
*/
@ExcelProperty(value = "分类ID")
private Long categoryId;
/**
* 分类名称
*/
@ExcelProperty(value = "分类名称")
private String categoryName;
/**
* 分类编码
*/
@ExcelProperty(value = "分类编码")
private String code;
/**
* 备注
*/
@ExcelProperty(value = "备注")
private String remark;
}

View File

@@ -1,57 +0,0 @@
package com.klp.workflow.domain.vo;
import com.alibaba.excel.annotation.ExcelProperty;
import com.fasterxml.jackson.annotation.JsonFormat;
import lombok.Data;
import lombok.NoArgsConstructor;
import java.io.Serializable;
import java.util.Date;
/**
* 待签流程对象导出VO
*
* @author konbai
*/
@Data
@NoArgsConstructor
public class WfClaimTaskExportVo implements Serializable {
private static final long serialVersionUID = 1L;
/**
* 任务编号
*/
@ExcelProperty(value = "任务编号")
private String taskId;
/**
* 流程名称
*/
@ExcelProperty(value = "流程名称")
private String procDefName;
/**
* 任务节点
*/
@ExcelProperty(value = "任务节点")
private String taskName;
/**
* 流程版本
*/
@ExcelProperty(value = "流程版本")
private int procDefVersion;
/**
* 流程发起人名称
*/
@ExcelProperty(value = "流程发起人")
private String startUserName;
/**
* 接收时间
*/
@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
@ExcelProperty(value = "接收时间")
private Date createTime;
}

View File

@@ -1,32 +0,0 @@
package com.klp.workflow.domain.vo;
import lombok.Data;
import java.util.Date;
/**
* 流程批复视图对象
*
* @author konbai
* @createTime 2022/4/4 02:03
*/
@Data
public class WfCommentVo {
/**
* 审批类别
*/
private String type;
/**
* 批复内容
*/
private String message;
/**
* 批复时间
*/
private Date time;
}

View File

@@ -1,93 +0,0 @@
package com.klp.workflow.domain.vo;
import com.alibaba.excel.annotation.ExcelIgnoreUnannotated;
import com.alibaba.excel.annotation.ExcelProperty;
import lombok.Data;
import java.util.Date;
/**
* 流程抄送视图对象 wf_copy
*
* @author klp
* @date 2022-05-19
*/
@Data
@ExcelIgnoreUnannotated
public class WfCopyVo {
private static final long serialVersionUID = 1L;
/**
* 抄送主键
*/
@ExcelProperty(value = "抄送主键")
private Long copyId;
/**
* 抄送标题
*/
@ExcelProperty(value = "抄送标题")
private String title;
/**
* 流程主键
*/
@ExcelProperty(value = "流程主键")
private String processId;
/**
* 流程名称
*/
@ExcelProperty(value = "流程名称")
private String processName;
/**
* 流程分类主键
*/
@ExcelProperty(value = "流程分类主键")
private String categoryId;
/**
* 部署主键
*/
@ExcelProperty(value = "部署主键")
private String deploymentId;
/**
* 流程实例主键
*/
@ExcelProperty(value = "流程实例主键")
private String instanceId;
/**
* 任务主键
*/
@ExcelProperty(value = "任务主键")
private String taskId;
/**
* 用户主键
*/
@ExcelProperty(value = "用户主键")
private Long userId;
/**
* 发起人Id
*/
@ExcelProperty(value = "发起人主键")
private Long originatorId;
/**
* 发起人名称
*/
@ExcelProperty(value = "发起人名称")
private String originatorName;
/**
* 抄送时间(创建时间)
*/
@ExcelProperty(value = "抄送时间")
private Date createTime;
}

View File

@@ -1,85 +0,0 @@
package com.klp.workflow.domain.vo;
import com.alibaba.excel.annotation.ExcelIgnoreUnannotated;
import com.alibaba.excel.annotation.ExcelProperty;
import com.klp.common.annotation.ExcelDictFormat;
import com.klp.common.convert.ExcelDictConvert;
import lombok.Data;
import java.util.Date;
/**
* 流程定义视图对象 workflow_definition
*
* @author KonBAI
* @date 2022-01-17
*/
@Data
@ExcelIgnoreUnannotated
public class WfDefinitionVo {
private static final long serialVersionUID = 1L;
/**
* 流程定义ID
*/
@ExcelProperty(value = "流程定义ID")
private String definitionId;
/**
* 流程名称
*/
@ExcelProperty(value = "流程名称")
private String processName;
/**
* 流程Key
*/
@ExcelProperty(value = "流程Key")
private String processKey;
/**
* 分类编码
*/
@ExcelProperty(value = "分类编码")
private String category;
/**
* 版本
*/
@ExcelProperty(value = "版本")
private Integer version;
/**
* 表单ID
*/
@ExcelProperty(value = "表单ID")
private Long formId;
/**
* 表单名称
*/
@ExcelProperty(value = "表单名称")
private String formName;
/**
* 部署ID
*/
@ExcelProperty(value = "部署ID")
private String deploymentId;
/**
* 流程是否暂停true:挂起 false:激活
*/
@ExcelProperty(value = "流程是否挂起", converter = ExcelDictConvert.class)
@ExcelDictFormat(readConverterExp = "true=挂起,false=激活")
private Boolean suspended;
/**
* 部署时间
*/
@ExcelProperty(value = "部署时间")
private Date deploymentTime;
}

View File

@@ -1,45 +0,0 @@
package com.klp.workflow.domain.vo;
import lombok.Data;
/**
* 部署实例和表单关联视图对象
*
* @author KonBAI
* @createTime 2022/7/17 18:29
*/
@Data
public class WfDeployFormVo {
private static final long serialVersionUID = 1L;
/**
* 流程部署主键
*/
private String deployId;
/**
* 表单Key
*/
private String formKey;
/**
* 节点Key
*/
private String nodeKey;
/**
* 表单名称
*/
private String formName;
/**
* 节点名称
*/
private String nodeName;
/**
* 表单内容
*/
private String content;
}

View File

@@ -1,80 +0,0 @@
package com.klp.workflow.domain.vo;
import com.alibaba.excel.annotation.ExcelIgnoreUnannotated;
import com.alibaba.excel.annotation.ExcelProperty;
import lombok.Data;
import java.util.Date;
/**
* 流程部署视图对象
*
* @author KonBAI
* @date 2022-06-30
*/
@Data
@ExcelIgnoreUnannotated
public class WfDeployVo {
private static final long serialVersionUID = 1L;
/**
* 流程定义ID
*/
@ExcelProperty(value = "流程定义ID")
private String definitionId;
/**
* 流程名称
*/
@ExcelProperty(value = "流程名称")
private String processName;
/**
* 流程Key
*/
@ExcelProperty(value = "流程Key")
private String processKey;
/**
* 分类编码
*/
@ExcelProperty(value = "分类编码")
private String category;
/**
* 版本
*/
private Integer version;
/**
* 表单ID
*/
@ExcelProperty(value = "表单ID")
private Long formId;
/**
* 表单名称
*/
@ExcelProperty(value = "表单名称")
private String formName;
/**
* 部署ID
*/
@ExcelProperty(value = "部署ID")
private String deploymentId;
/**
* 流程定义状态: 1:激活 , 2:中止
*/
@ExcelProperty(value = "流程定义状态: 1:激活 , 2:中止")
private Boolean suspended;
/**
* 部署时间
*/
@ExcelProperty(value = "部署时间")
private Date deploymentTime;
}

View File

@@ -1,47 +0,0 @@
package com.klp.workflow.domain.vo;
import cn.hutool.core.util.ObjectUtil;
import com.klp.flowable.core.FormConf;
import lombok.Data;
import java.util.List;
/**
* 流程详情视图对象
*
* @author KonBAI
* @createTime 2022/8/7 15:01
*/
@Data
public class WfDetailVo {
/**
* 任务表单信息
*/
private FormConf taskFormData;
/**
* 历史流程节点信息
*/
private List<WfProcNodeVo> historyProcNodeList;
/**
* 流程表单列表
*/
private List<FormConf> processFormList;
/**
* 流程XML
*/
private String bpmnXml;
private WfViewerVo flowViewer;
/**
* 是否存在任务表单信息
* @return true:存在false:不存在
*/
public Boolean isExistTaskForm() {
return ObjectUtil.isNotEmpty(this.taskFormData);
}
}

View File

@@ -1,70 +0,0 @@
package com.klp.workflow.domain.vo;
import com.alibaba.excel.annotation.ExcelProperty;
import com.fasterxml.jackson.annotation.JsonFormat;
import lombok.Data;
import lombok.NoArgsConstructor;
import java.io.Serializable;
import java.util.Date;
/**
* 已办流程对象导出VO
*
* @author konbai
*/
@Data
@NoArgsConstructor
public class WfFinishedTaskExportVo implements Serializable {
private static final long serialVersionUID = 1L;
/**
* 任务编号
*/
@ExcelProperty(value = "任务编号")
private String taskId;
/**
* 流程名称
*/
@ExcelProperty(value = "流程名称")
private String procDefName;
/**
* 任务节点
*/
@ExcelProperty(value = "任务节点")
private String taskName;
/**
* 流程版本
*/
@ExcelProperty(value = "流程版本")
private int procDefVersion;
/**
* 流程发起人名称
*/
@ExcelProperty(value = "流程发起人")
private String startUserName;
/**
* 接收时间
*/
@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
@ExcelProperty(value = "接收时间")
private Date createTime;
/**
* 审批时间
*/
@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
@ExcelProperty(value = "审批时间")
private Date finishTime;
/**
* 任务耗时
*/
@ExcelProperty(value = "任务耗时")
private String duration;
}

View File

@@ -1,42 +0,0 @@
package com.klp.workflow.domain.vo;
import com.alibaba.excel.annotation.ExcelIgnoreUnannotated;
import com.alibaba.excel.annotation.ExcelProperty;
import lombok.Data;
/**
* 流程分类视图对象
*
* @author KonBAI
* @createTime 2022/3/7 22:07
*/
@Data
@ExcelIgnoreUnannotated
public class WfFormVo {
private static final long serialVersionUID = 1L;
/**
* 表单主键
*/
@ExcelProperty(value = "表单ID")
private Long formId;
/**
* 表单名称
*/
@ExcelProperty(value = "表单名称")
private String formName;
/**
* 表单内容
*/
@ExcelProperty(value = "表单内容")
private String content;
/**
* 备注
*/
@ExcelProperty(value = "备注")
private String remark;
}

View File

@@ -1,59 +0,0 @@
package com.klp.workflow.domain.vo;
import com.alibaba.excel.annotation.ExcelProperty;
import lombok.Data;
import lombok.NoArgsConstructor;
import java.io.Serializable;
import java.util.Date;
/**
* 流程模型对象导出VO
*
* @author konbai
*/
@Data
@NoArgsConstructor
public class WfModelExportVo implements Serializable {
private static final long serialVersionUID = 1L;
/**
* 模型ID
*/
@ExcelProperty(value = "模型ID")
private String modelId;
/**
* 模型Key
*/
@ExcelProperty(value = "模型Key")
private String modelKey;
/**
* 模型名称
*/
@ExcelProperty(value = "模型名称")
private String modelName;
/**
* 分类编码
*/
@ExcelProperty(value = "分类编码")
private String category;
/**
* 流程分类
*/
@ExcelProperty(value = "流程分类")
private String categoryName;
/**
* 模型版本
*/
@ExcelProperty(value = "模型版本")
private Integer version;
/**
* 模型描述
*/
@ExcelProperty(value = "模型描述")
private String description;
/**
* 创建时间
*/
@ExcelProperty(value = "创建时间")
private Date createTime;
}

View File

@@ -1,59 +0,0 @@
package com.klp.workflow.domain.vo;
import lombok.Data;
import java.util.Date;
/**
* 流程模型视图对象
*
* @author KonBAI
* @createTime 2022/6/21 9:16
*/
@Data
public class WfModelVo {
/**
* 模型ID
*/
private String modelId;
/**
* 模型名称
*/
private String modelName;
/**
* 模型Key
*/
private String modelKey;
/**
* 分类编码
*/
private String category;
/**
* 版本
*/
private Integer version;
/**
* 表单类型
*/
private Integer formType;
/**
* 表单ID
*/
private Long formId;
/**
* 模型描述
*/
private String description;
/**
* 创建时间
*/
private Date createTime;
/**
* 流程xml
*/
private String bpmnXml;
/**
* 表单内容
*/
private String content;
}

View File

@@ -1,75 +0,0 @@
package com.klp.workflow.domain.vo;
import com.alibaba.excel.annotation.ExcelProperty;
import com.fasterxml.jackson.annotation.JsonFormat;
import lombok.Data;
import lombok.NoArgsConstructor;
import java.io.Serializable;
import java.util.Date;
/**
* 我拥有流程对象导出VO
*
* @author konbai
*/
@Data
@NoArgsConstructor
public class WfOwnTaskExportVo implements Serializable {
private static final long serialVersionUID = 1L;
/**
* 流程实例ID
*/
@ExcelProperty(value = "流程编号")
private String procInsId;
/**
* 流程名称
*/
@ExcelProperty(value = "流程名称")
private String procDefName;
/**
* 流程类别
*/
@ExcelProperty(value = "流程类别")
private String category;
/**
* 流程版本
*/
@ExcelProperty(value = "流程版本")
private int procDefVersion;
/**
* 提交时间
*/
@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
@ExcelProperty(value = "提交时间")
private Date createTime;
/**
* 流程状态
*/
@ExcelProperty(value = "流程状态")
private String status;
/**
* 任务耗时
*/
@ExcelProperty(value = "任务耗时")
private String duration;
/**
* 当前节点
*/
@ExcelProperty(value = "当前节点")
private String taskName;
/**
* 任务完成时间
*/
@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
private Date finishTime;
}

View File

@@ -1,67 +0,0 @@
package com.klp.workflow.domain.vo;
import com.alibaba.excel.annotation.ExcelIgnoreUnannotated;
import com.fasterxml.jackson.annotation.JsonFormat;
import lombok.Data;
import org.flowable.engine.task.Comment;
import java.io.Serializable;
import java.util.Date;
import java.util.List;
/**
* 工作流节点元素视图对象
*
* @author KonBAI
* @createTime 2022/9/11 22:04
*/
@Data
@ExcelIgnoreUnannotated
public class WfProcNodeVo implements Serializable {
/**
* 流程ID
*/
private String procDefId;
/**
* 活动ID
*/
private String activityId;
/**
* 活动名称
*/
private String activityName;
/**
* 活动类型
*/
private String activityType;
/**
* 活动耗时
*/
private String duration;
/**
* 执行人Id
*/
private Long assigneeId;
/**
* 执行人名称
*/
private String assigneeName;
/**
* 候选执行人
*/
private String candidate;
/**
* 任务意见
*/
private List<Comment> commentList;
/**
* 创建时间
*/
@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
private Date createTime;
/**
* 结束时间
*/
@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
private Date endTime;
}

View File

@@ -1,132 +0,0 @@
package com.klp.workflow.domain.vo;
import com.alibaba.excel.annotation.ExcelIgnoreUnannotated;
import com.fasterxml.jackson.annotation.JsonFormat;
import com.klp.workflow.domain.dto.WfCommentDto;
import lombok.Data;
import org.flowable.engine.task.Comment;
import java.io.Serializable;
import java.util.Date;
import java.util.List;
/**
* 工作流任务视图对象
*
* @author KonBAI
* @createTime 2022/3/10 00:12
*/
@Data
@ExcelIgnoreUnannotated
public class WfTaskVo implements Serializable {
/**
* 任务编号
*/
private String taskId;
/**
* 任务名称
*/
private String taskName;
/**
* 任务Key
*/
private String taskDefKey;
/**
* 任务执行人Id
*/
private Long assigneeId;
/**
* 部门名称
*/
@Deprecated
private String deptName;
/**
* 流程发起人部门名称
*/
@Deprecated
private String startDeptName;
/**
* 任务执行人名称
*/
private String assigneeName;
/**
* 流程发起人Id
*/
private Long startUserId;
/**
* 流程发起人名称
*/
private String startUserName;
/**
* 流程类型
*/
private String category;
/**
* 流程变量信息
*/
private Object procVars;
/**
* 局部变量信息
*/
private Object taskLocalVars;
/**
* 流程部署编号
*/
private String deployId;
/**
* 流程ID
*/
private String procDefId;
/**
* 流程key
*/
private String procDefKey;
/**
* 流程定义名称
*/
private String procDefName;
/**
* 流程定义内置使用版本
*/
private int procDefVersion;
/**
* 流程实例ID
*/
private String procInsId;
/**
* 历史流程实例ID
*/
private String hisProcInsId;
/**
* 任务耗时
*/
private String duration;
/**
* 任务意见
*/
private WfCommentDto comment;
/**
* 任务意见
*/
private List<Comment> commentList;
/**
* 候选执行人
*/
private String candidate;
/**
* 任务创建时间
*/
@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
private Date createTime;
/**
* 任务完成时间
*/
@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
private Date finishTime;
/**
* 流程状态
*/
private String processStatus;
}

View File

@@ -1,57 +0,0 @@
package com.klp.workflow.domain.vo;
import com.alibaba.excel.annotation.ExcelProperty;
import com.fasterxml.jackson.annotation.JsonFormat;
import lombok.Data;
import lombok.NoArgsConstructor;
import java.io.Serializable;
import java.util.Date;
/**
* 待办流程对象导出VO
*
* @author konbai
*/
@Data
@NoArgsConstructor
public class WfTodoTaskExportVo implements Serializable {
private static final long serialVersionUID = 1L;
/**
* 任务编号
*/
@ExcelProperty(value = "任务编号")
private String taskId;
/**
* 流程名称
*/
@ExcelProperty(value = "流程名称")
private String procDefName;
/**
* 任务节点
*/
@ExcelProperty(value = "任务节点")
private String taskName;
/**
* 流程版本
*/
@ExcelProperty(value = "流程版本")
private int procDefVersion;
/**
* 流程发起人名称
*/
@ExcelProperty(value = "流程发起人")
private String startUserName;
/**
* 接收时间
*/
@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
@ExcelProperty(value = "接收时间")
private Date createTime;
}

View File

@@ -1,41 +0,0 @@
package com.klp.workflow.domain.vo;
import com.alibaba.excel.annotation.ExcelIgnoreUnannotated;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
import java.util.Set;
/**
* 任务追踪视图对象
*
* @author KonBAI
* @createTime 2022/1/8 19:42
*/
@Data
@NoArgsConstructor
@AllArgsConstructor
@ExcelIgnoreUnannotated
public class WfViewerVo {
/**
* 获取流程实例的历史节点(去重)
*/
private Set<String> finishedTaskSet;
/**
* 已完成
*/
private Set<String> finishedSequenceFlowSet;
/**
* 获取流程实例当前正在待办的节点(去重)
*/
private Set<String> unfinishedTaskSet;
/**
* 已拒绝
*/
private Set<String> rejectedTaskSet;
}

View File

@@ -1,62 +0,0 @@
package com.klp.workflow.handler;
import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.util.ObjectUtil;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.baomidou.mybatisplus.extension.toolkit.SimpleQuery;
import com.klp.common.core.domain.entity.SysUser;
import com.klp.flowable.common.constant.ProcessConstants;
import com.klp.system.domain.SysUserRole;
import lombok.AllArgsConstructor;
import org.flowable.bpmn.model.FlowElement;
import org.flowable.bpmn.model.UserTask;
import org.flowable.engine.delegate.DelegateExecution;
import org.springframework.stereotype.Component;
import java.util.ArrayList;
import java.util.LinkedHashSet;
import java.util.List;
import java.util.Set;
import java.util.stream.Collectors;
/**
* 多实例处理类
*
* @author KonBAI
*/
@AllArgsConstructor
@Component("multiInstanceHandler")
public class MultiInstanceHandler {
public Set<String> getUserIds(DelegateExecution execution) {
Set<String> candidateUserIds = new LinkedHashSet<>();
FlowElement flowElement = execution.getCurrentFlowElement();
if (ObjectUtil.isNotEmpty(flowElement) && flowElement instanceof UserTask) {
UserTask userTask = (UserTask) flowElement;
String dataType = userTask.getAttributeValue(ProcessConstants.NAMASPASE, ProcessConstants.PROCESS_CUSTOM_DATA_TYPE);
if ("USERS".equals(dataType) && CollUtil.isNotEmpty(userTask.getCandidateUsers())) {
// 添加候选用户id
candidateUserIds.addAll(userTask.getCandidateUsers());
} else if (CollUtil.isNotEmpty(userTask.getCandidateGroups())) {
// 获取组的ID角色ID集合或部门ID集合
List<Long> groups = userTask.getCandidateGroups().stream()
.map(item -> Long.parseLong(item.substring(4)))
.collect(Collectors.toList());
List<Long> userIds = new ArrayList<>();
if ("ROLES".equals(dataType)) {
// 通过角色id获取所有用户id集合
LambdaQueryWrapper<SysUserRole> lqw = Wrappers.lambdaQuery(SysUserRole.class).select(SysUserRole::getUserId).in(SysUserRole::getRoleId, groups);
userIds = SimpleQuery.list(lqw, SysUserRole::getUserId);
} else if ("DEPTS".equals(dataType)) {
// 通过部门id获取所有用户id集合
LambdaQueryWrapper<SysUser> lqw = Wrappers.lambdaQuery(SysUser.class).select(SysUser::getUserId).in(SysUser::getDeptId, groups);
userIds = SimpleQuery.list(lqw, SysUser::getUserId);
}
// 添加候选用户id
userIds.forEach(id -> candidateUserIds.add(String.valueOf(id)));
}
}
return candidateUserIds;
}
}

View File

@@ -1,15 +0,0 @@
package com.klp.workflow.mapper;
import com.klp.common.core.mapper.BaseMapperPlus;
import com.klp.workflow.domain.WfCategory;
import com.klp.workflow.domain.vo.WfCategoryVo;
/**
* 流程分类Mapper接口
*
* @author KonBAI
* @date 2022-01-15
*/
public interface WfCategoryMapper extends BaseMapperPlus<WfCategoryMapper, WfCategory, WfCategoryVo> {
}

View File

@@ -1,15 +0,0 @@
package com.klp.workflow.mapper;
import com.klp.common.core.mapper.BaseMapperPlus;
import com.klp.workflow.domain.WfCopy;
import com.klp.workflow.domain.vo.WfCopyVo;
/**
* 流程抄送Mapper接口
*
* @author KonBAI
* @date 2022-05-19
*/
public interface WfCopyMapper extends BaseMapperPlus<WfCopyMapper, WfCopy, WfCopyVo> {
}

View File

@@ -1,15 +0,0 @@
package com.klp.workflow.mapper;
import com.klp.common.core.mapper.BaseMapperPlus;
import com.klp.workflow.domain.WfDeployForm;
import com.klp.workflow.domain.vo.WfDeployFormVo;
/**
* 流程实例关联表单Mapper接口
*
* @author KonBAI
* @createTime 2022/3/7 22:07
*/
public interface WfDeployFormMapper extends BaseMapperPlus<WfDeployFormMapper, WfDeployForm, WfDeployFormVo> {
}

View File

@@ -1,21 +0,0 @@
package com.klp.workflow.mapper;
import com.baomidou.mybatisplus.core.conditions.Wrapper;
import com.baomidou.mybatisplus.core.toolkit.Constants;
import com.klp.common.core.mapper.BaseMapperPlus;
import com.klp.workflow.domain.WfForm;
import com.klp.workflow.domain.vo.WfFormVo;
import org.apache.ibatis.annotations.Param;
import java.util.List;
/**
* 流程表单Mapper接口
*
* @author KonBAI
* @createTime 2022/3/7 22:07
*/
public interface WfFormMapper extends BaseMapperPlus<WfFormMapper, WfForm, WfFormVo> {
List<WfFormVo> selectFormVoList(@Param(Constants.WRAPPER) Wrapper<WfForm> queryWrapper);
}

View File

@@ -1,64 +0,0 @@
package com.klp.workflow.service;
import com.klp.common.core.domain.PageQuery;
import com.klp.common.core.page.TableDataInfo;
import com.klp.workflow.domain.WfCategory;
import com.klp.workflow.domain.vo.WfCategoryVo;
import java.util.Collection;
import java.util.List;
/**
* 流程分类Service接口
*
* @author KonBAI
* @date 2022-01-15
*/
public interface IWfCategoryService {
/**
* 查询单个
* @return
*/
WfCategoryVo queryById(Long categoryId);
/**
* 查询列表
*/
TableDataInfo<WfCategoryVo> queryPageList(WfCategory category, PageQuery pageQuery);
/**
* 查询列表
*/
List<WfCategoryVo> queryList(WfCategory category);
/**
* 新增流程分类
*
* @param category 流程分类信息
* @return 结果
*/
int insertCategory(WfCategory category);
/**
* 编辑流程分类
* @param category 流程分类信息
* @return 结果
*/
int updateCategory(WfCategory category);
/**
* 校验并删除数据
* @param ids 主键集合
* @param isValid 是否校验,true-删除前校验,false-不校验
* @return 结果
*/
int deleteWithValidByIds(Collection<Long> ids, Boolean isValid);
/**
* 校验分类编码是否唯一
*
* @param category 流程分类
* @return 结果
*/
boolean checkCategoryCodeUnique(WfCategory category);
}

View File

@@ -1,49 +0,0 @@
package com.klp.workflow.service;
import com.klp.common.core.domain.PageQuery;
import com.klp.common.core.page.TableDataInfo;
import com.klp.workflow.domain.bo.WfCopyBo;
import com.klp.workflow.domain.bo.WfTaskBo;
import com.klp.workflow.domain.vo.WfCopyVo;
import java.util.List;
/**
* 流程抄送Service接口
*
* @author KonBAI
* @date 2022-05-19
*/
public interface IWfCopyService {
/**
* 查询流程抄送
*
* @param copyId 流程抄送主键
* @return 流程抄送
*/
WfCopyVo queryById(Long copyId);
/**
* 查询流程抄送列表
*
* @param wfCopy 流程抄送
* @return 流程抄送集合
*/
TableDataInfo<WfCopyVo> selectPageList(WfCopyBo wfCopy, PageQuery pageQuery);
/**
* 查询流程抄送列表
*
* @param wfCopy 流程抄送
* @return 流程抄送集合
*/
List<WfCopyVo> selectList(WfCopyBo wfCopy);
/**
* 抄送
* @param taskBo
* @return
*/
Boolean makeCopy(WfTaskBo taskBo);
}

View File

@@ -1,39 +0,0 @@
package com.klp.workflow.service;
import com.klp.workflow.domain.WfDeployForm;
import com.klp.workflow.domain.vo.WfFormVo;
import org.flowable.bpmn.model.BpmnModel;
/**
* 流程实例关联表单Service接口
*
* @author KonBAI
* @createTime 2022/3/7 22:07
*/
public interface IWfDeployFormService {
/**
* 新增流程实例关联表单
*
* @param wfDeployForm 流程实例关联表单
* @return 结果
*/
int insertWfDeployForm(WfDeployForm wfDeployForm);
/**
* 保存流程实例关联表单
* @param deployId 部署ID
* @param bpmnModel bpmnModel对象
* @return
*/
boolean saveInternalDeployForm(String deployId, BpmnModel bpmnModel);
/**
* 查询流程挂着的表单
*
* @param deployId
* @return
*/
@Deprecated
WfFormVo selectDeployFormByDeployId(String deployId);
}

View File

@@ -1,25 +0,0 @@
package com.klp.workflow.service;
import com.klp.common.core.domain.PageQuery;
import com.klp.common.core.page.TableDataInfo;
import com.klp.flowable.core.domain.ProcessQuery;
import com.klp.workflow.domain.vo.WfDeployVo;
import java.util.List;
/**
* @author KonBAI
* @createTime 2022/6/30 9:03
*/
public interface IWfDeployService {
TableDataInfo<WfDeployVo> queryPageList(ProcessQuery processQuery, PageQuery pageQuery);
TableDataInfo<WfDeployVo> queryPublishList(String processKey, PageQuery pageQuery);
void updateState(String definitionId, String stateCode);
String queryBpmnXmlById(String definitionId);
void deleteByIds(List<String> deployIds);
}

View File

@@ -1,65 +0,0 @@
package com.klp.workflow.service;
import com.klp.common.core.domain.PageQuery;
import com.klp.common.core.page.TableDataInfo;
import com.klp.workflow.domain.bo.WfFormBo;
import com.klp.workflow.domain.vo.WfFormVo;
import java.util.Collection;
import java.util.List;
/**
* 表单
*
* @author KonBAI
* @createTime 2022/3/7 22:07
*/
public interface IWfFormService {
/**
* 查询流程表单
*
* @param formId 流程表单ID
* @return 流程表单
*/
WfFormVo queryById(Long formId);
/**
* 查询流程表单列表
*
* @param bo 流程表单
* @return 流程表单集合
*/
TableDataInfo<WfFormVo> queryPageList(WfFormBo bo, PageQuery pageQuery);
/**
* 查询流程表单列表
*
* @param bo 流程表单
* @return 流程表单集合
*/
List<WfFormVo> queryList(WfFormBo bo);
/**
* 新增流程表单
*
* @param bo 流程表单
* @return 结果
*/
int insertForm(WfFormBo bo);
/**
* 修改流程表单
*
* @param bo 流程表单
* @return 结果
*/
int updateForm(WfFormBo bo);
/**
* 批量删除流程表单
*
* @param formIds 需要删除的流程表单ID
* @return 结果
*/
Boolean deleteWithValidByIds(Collection<Long> formIds);
}

View File

@@ -1,52 +0,0 @@
package com.klp.workflow.service;
import com.klp.workflow.domain.bo.WfTaskBo;
import org.flowable.engine.history.HistoricProcessInstance;
import java.util.Map;
/**
* @author KonBAI
* @createTime 2022/3/10 00:12
*/
public interface IWfInstanceService {
/**
* 结束流程实例
*
* @param vo
*/
void stopProcessInstance(WfTaskBo vo);
/**
* 激活或挂起流程实例
*
* @param state 状态
* @param instanceId 流程实例ID
*/
void updateState(Integer state, String instanceId);
/**
* 删除流程实例ID
*
* @param instanceId 流程实例ID
* @param deleteReason 删除原因
*/
void delete(String instanceId, String deleteReason);
/**
* 根据实例ID查询历史实例数据
*
* @param processInstanceId
* @return
*/
HistoricProcessInstance getHistoricProcessInstanceById(String processInstanceId);
/**
* 查询流程详情信息
* @param procInsId 流程实例ID
* @param deployId 流程部署ID
*/
Map<String, Object> queryDetailProcess(String procInsId, String deployId);
}

View File

@@ -1,71 +0,0 @@
package com.klp.workflow.service;
import com.klp.common.core.domain.PageQuery;
import com.klp.common.core.page.TableDataInfo;
import com.klp.workflow.domain.bo.WfModelBo;
import com.klp.workflow.domain.vo.WfModelVo;
import java.util.Collection;
import java.util.List;
/**
* @author KonBAI
* @createTime 2022/6/21 9:11
*/
public interface IWfModelService {
/**
* 查询流程模型列表
*/
TableDataInfo<WfModelVo> list(WfModelBo modelBo, PageQuery pageQuery);
/**
* 查询流程模型列表
*/
List<WfModelVo> list(WfModelBo modelBo);
/**
* 查询流程模型列表
*/
TableDataInfo<WfModelVo> historyList(WfModelBo modelBo, PageQuery pageQuery);
/**
* 查询流程模型详情信息
*/
WfModelVo getModel(String modelId);
/**
* 查询流程表单详细信息
*/
String queryBpmnXmlById(String modelId);
/**
* 新增模型信息
*/
void insertModel(WfModelBo modelBo);
/**
* 修改模型信息
*/
void updateModel(WfModelBo modelBo);
/**
* 保存流程模型信息
*/
void saveModel(WfModelBo modelBo);
/**
* 设为最新流程模型
*/
void latestModel(String modelId);
/**
* 删除流程模型
*/
void deleteByIds(Collection<String> ids);
/**
* 部署流程模型
*/
boolean deployModel(String modelId);
}

View File

@@ -1,116 +0,0 @@
package com.klp.workflow.service;
import com.klp.common.core.domain.PageQuery;
import com.klp.common.core.page.TableDataInfo;
import com.klp.flowable.core.FormConf;
import com.klp.flowable.core.domain.ProcessQuery;
import com.klp.workflow.domain.vo.WfDefinitionVo;
import com.klp.workflow.domain.vo.WfDetailVo;
import com.klp.workflow.domain.vo.WfTaskVo;
import java.util.List;
import java.util.Map;
/**
* @author KonBAI
* @createTime 2022/3/24 18:57
*/
public interface IWfProcessService {
/**
* 查询可发起流程列表
* @param pageQuery 分页参数
* @return
*/
TableDataInfo<WfDefinitionVo> selectPageStartProcessList(ProcessQuery processQuery, PageQuery pageQuery);
/**
* 查询可发起流程列表
*/
List<WfDefinitionVo> selectStartProcessList(ProcessQuery processQuery);
/**
* 查询我的流程列表
* @param pageQuery 分页参数
*/
TableDataInfo<WfTaskVo> selectPageOwnProcessList(ProcessQuery processQuery, PageQuery pageQuery);
/**
* 查询我的流程列表
*/
List<WfTaskVo> selectOwnProcessList(ProcessQuery processQuery);
/**
* 查询代办任务列表
* @param pageQuery 分页参数
*/
TableDataInfo<WfTaskVo> selectPageTodoProcessList(ProcessQuery processQuery, PageQuery pageQuery);
/**
* 查询代办任务列表
*/
List<WfTaskVo> selectTodoProcessList(ProcessQuery processQuery);
/**
* 查询待签任务列表
* @param pageQuery 分页参数
*/
TableDataInfo<WfTaskVo> selectPageClaimProcessList(ProcessQuery processQuery, PageQuery pageQuery);
/**
* 查询待签任务列表
*/
List<WfTaskVo> selectClaimProcessList(ProcessQuery processQuery);
/**
* 查询已办任务列表
* @param pageQuery 分页参数
*/
TableDataInfo<WfTaskVo> selectPageFinishedProcessList(ProcessQuery processQuery, PageQuery pageQuery);
/**
* 查询已办任务列表
*/
List<WfTaskVo> selectFinishedProcessList(ProcessQuery processQuery);
/**
* 查询流程部署关联表单信息
* @param definitionId 流程定义ID
* @param deployId 部署ID
*/
FormConf selectFormContent(String definitionId, String deployId, String procInsId);
/**
* 启动流程实例
* @param procDefId 流程定义ID
* @param variables 扩展参数
*/
void startProcessByDefId(String procDefId, Map<String, Object> variables);
/**
* 通过DefinitionKey启动流程
* @param procDefKey 流程定义Key
* @param variables 扩展参数
*/
void startProcessByDefKey(String procDefKey, Map<String, Object> variables);
/**
* 删除流程实例
*/
void deleteProcessByIds(String[] instanceIds);
/**
* 读取xml文件
* @param processDefId 流程定义ID
*/
String queryBpmnXmlById(String processDefId);
/**
* 查询流程任务详情信息
* @param procInsId 流程实例ID
* @param taskId 任务ID
*/
WfDetailVo queryProcessDetail(String procInsId, String taskId);
}

View File

@@ -1,117 +0,0 @@
package com.klp.workflow.service;
import com.klp.workflow.domain.bo.WfTaskBo;
import org.flowable.bpmn.model.FlowElement;
import org.flowable.engine.runtime.ProcessInstance;
import java.io.InputStream;
import java.util.List;
import java.util.Map;
/**
* @author KonBAI
* @createTime 2022/3/10 00:12
*/
public interface IWfTaskService {
/**
* 审批任务
*
* @param task 请求实体参数
*/
void complete(WfTaskBo task);
/**
* 拒绝任务
*
* @param taskBo
*/
void taskReject(WfTaskBo taskBo);
/**
* 退回任务
*
* @param bo 请求实体参数
*/
void taskReturn(WfTaskBo bo);
/**
* 获取所有可回退的节点
*
* @param bo
* @return
*/
List<FlowElement> findReturnTaskList(WfTaskBo bo);
/**
* 删除任务
*
* @param bo 请求实体参数
*/
void deleteTask(WfTaskBo bo);
/**
* 认领/签收任务
*
* @param bo 请求实体参数
*/
void claim(WfTaskBo bo);
/**
* 取消认领/签收任务
*
* @param bo 请求实体参数
*/
void unClaim(WfTaskBo bo);
/**
* 委派任务
*
* @param bo 请求实体参数
*/
void delegateTask(WfTaskBo bo);
/**
* 转办任务
*
* @param bo 请求实体参数
*/
void transferTask(WfTaskBo bo);
/**
* 取消申请
* @param bo
* @return
*/
void stopProcess(WfTaskBo bo);
/**
* 撤回流程
* @param bo
* @return
*/
void revokeProcess(WfTaskBo bo);
/**
* 获取流程过程图
* @param processId
* @return
*/
InputStream diagram(String processId);
/**
* 获取流程变量
* @param taskId 任务ID
* @return 流程变量
*/
Map<String, Object> getProcessVariables(String taskId);
/**
* 启动第一个任务
* @param processInstance 流程实例
* @param variables 流程参数
*/
void startFirstTask(ProcessInstance processInstance, Map<String, Object> variables);
}

View File

@@ -1,93 +0,0 @@
package com.klp.workflow.service.impl;
import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.util.ObjectUtil;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.klp.common.core.domain.PageQuery;
import com.klp.common.core.page.TableDataInfo;
import com.klp.common.utils.StringUtils;
import com.klp.workflow.domain.WfCategory;
import com.klp.workflow.domain.vo.WfCategoryVo;
import com.klp.workflow.mapper.WfCategoryMapper;
import com.klp.workflow.service.IWfCategoryService;
import lombok.RequiredArgsConstructor;
import org.springframework.stereotype.Service;
import java.util.Collection;
import java.util.List;
import java.util.Map;
/**
* 流程分类Service业务层处理
*
* @author KonBAI
* @date 2022-01-15
*/
@RequiredArgsConstructor
@Service
public class WfCategoryServiceImpl implements IWfCategoryService {
private final WfCategoryMapper baseMapper;
@Override
public WfCategoryVo queryById(Long categoryId){
return baseMapper.selectVoById(categoryId);
}
@Override
public TableDataInfo<WfCategoryVo> queryPageList(WfCategory category, PageQuery pageQuery) {
LambdaQueryWrapper<WfCategory> lqw = buildQueryWrapper(category);
Page<WfCategoryVo> result = baseMapper.selectVoPage(pageQuery.build(), lqw);
return TableDataInfo.build(result);
}
@Override
public List<WfCategoryVo> queryList(WfCategory category) {
LambdaQueryWrapper<WfCategory> lqw = buildQueryWrapper(category);
return baseMapper.selectVoList(lqw);
}
private LambdaQueryWrapper<WfCategory> buildQueryWrapper(WfCategory category) {
Map<String, Object> params = category.getParams();
LambdaQueryWrapper<WfCategory> lqw = Wrappers.lambdaQuery();
lqw.like(StringUtils.isNotBlank(category.getCategoryName()), WfCategory::getCategoryName, category.getCategoryName());
lqw.eq(StringUtils.isNotBlank(category.getCode()), WfCategory::getCode, category.getCode());
return lqw;
}
@Override
public int insertCategory(WfCategory categoryBo) {
WfCategory add = BeanUtil.toBean(categoryBo, WfCategory.class);
return baseMapper.insert(add);
}
@Override
public int updateCategory(WfCategory categoryBo) {
WfCategory update = BeanUtil.toBean(categoryBo, WfCategory.class);
return baseMapper.updateById(update);
}
@Override
public int deleteWithValidByIds(Collection<Long> ids, Boolean isValid) {
if(isValid){
//TODO 做一些业务上的校验,判断是否需要校验
}
return baseMapper.deleteBatchIds(ids);
}
/**
* 校验分类编码是否唯一
*
* @param category 流程分类
* @return 结果
*/
@Override
public boolean checkCategoryCodeUnique(WfCategory category) {
boolean exist = baseMapper.exists(new LambdaQueryWrapper<WfCategory>()
.eq(WfCategory::getCode, category.getCode())
.ne(ObjectUtil.isNotNull(category.getCategoryId()), WfCategory::getCategoryId, category.getCategoryId()));
return !exist;
}
}

View File

@@ -1,114 +0,0 @@
package com.klp.workflow.service.impl;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.klp.common.core.domain.PageQuery;
import com.klp.common.core.page.TableDataInfo;
import com.klp.common.helper.LoginHelper;
import com.klp.common.utils.StringUtils;
import com.klp.workflow.domain.WfCopy;
import com.klp.workflow.domain.bo.WfCopyBo;
import com.klp.workflow.domain.bo.WfTaskBo;
import com.klp.workflow.domain.vo.WfCopyVo;
import com.klp.workflow.mapper.WfCopyMapper;
import com.klp.workflow.service.IWfCopyService;
import lombok.RequiredArgsConstructor;
import org.flowable.engine.HistoryService;
import org.flowable.engine.history.HistoricProcessInstance;
import org.springframework.stereotype.Service;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
/**
* 流程抄送Service业务层处理
*
* @author KonBAI
* @date 2022-05-19
*/
@RequiredArgsConstructor
@Service
public class WfCopyServiceImpl implements IWfCopyService {
private final WfCopyMapper baseMapper;
private final HistoryService historyService;
/**
* 查询流程抄送
*
* @param copyId 流程抄送主键
* @return 流程抄送
*/
@Override
public WfCopyVo queryById(Long copyId){
return baseMapper.selectVoById(copyId);
}
/**
* 查询流程抄送列表
*
* @param bo 流程抄送
* @return 流程抄送
*/
@Override
public TableDataInfo<WfCopyVo> selectPageList(WfCopyBo bo, PageQuery pageQuery) {
LambdaQueryWrapper<WfCopy> lqw = buildQueryWrapper(bo);
lqw.orderByDesc(WfCopy::getCreateTime);
Page<WfCopyVo> result = baseMapper.selectVoPage(pageQuery.build(), lqw);
return TableDataInfo.build(result);
}
/**
* 查询流程抄送列表
*
* @param bo 流程抄送
* @return 流程抄送
*/
@Override
public List<WfCopyVo> selectList(WfCopyBo bo) {
LambdaQueryWrapper<WfCopy> lqw = buildQueryWrapper(bo);
return baseMapper.selectVoList(lqw);
}
private LambdaQueryWrapper<WfCopy> buildQueryWrapper(WfCopyBo bo) {
Map<String, Object> params = bo.getParams();
LambdaQueryWrapper<WfCopy> lqw = Wrappers.lambdaQuery();
lqw.eq(bo.getUserId() != null, WfCopy::getUserId, bo.getUserId());
lqw.like(StringUtils.isNotBlank(bo.getProcessName()), WfCopy::getProcessName, bo.getProcessName());
lqw.like(StringUtils.isNotBlank(bo.getOriginatorName()), WfCopy::getOriginatorName, bo.getOriginatorName());
return lqw;
}
@Override
public Boolean makeCopy(WfTaskBo taskBo) {
if (StringUtils.isBlank(taskBo.getCopyUserIds())) {
// 若抄送用户为空,则不需要处理,返回成功
return true;
}
HistoricProcessInstance historicProcessInstance = historyService.createHistoricProcessInstanceQuery()
.processInstanceId(taskBo.getProcInsId()).singleResult();
String[] ids = taskBo.getCopyUserIds().split(",");
List<WfCopy> copyList = new ArrayList<>(ids.length);
Long originatorId = LoginHelper.getUserId();
String originatorName = LoginHelper.getNickName();
String title = historicProcessInstance.getProcessDefinitionName() + "-" + taskBo.getTaskName();
for (String id : ids) {
Long userId = Long.valueOf(id);
WfCopy copy = new WfCopy();
copy.setTitle(title);
copy.setProcessId(historicProcessInstance.getProcessDefinitionId());
copy.setProcessName(historicProcessInstance.getProcessDefinitionName());
copy.setDeploymentId(historicProcessInstance.getDeploymentId());
copy.setInstanceId(taskBo.getProcInsId());
copy.setTaskId(taskBo.getTaskId());
copy.setUserId(userId);
copy.setOriginatorId(originatorId);
copy.setOriginatorName(originatorName);
copyList.add(copy);
}
return baseMapper.insertBatch(copyList);
}
}

View File

@@ -1,134 +0,0 @@
package com.klp.workflow.service.impl;
import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.convert.Convert;
import cn.hutool.core.util.ObjectUtil;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.klp.common.exception.ServiceException;
import com.klp.common.utils.StringUtils;
import com.klp.flowable.utils.ModelUtils;
import com.klp.workflow.domain.WfDeployForm;
import com.klp.workflow.domain.WfForm;
import com.klp.workflow.domain.vo.WfFormVo;
import com.klp.workflow.mapper.WfDeployFormMapper;
import com.klp.workflow.mapper.WfFormMapper;
import com.klp.workflow.service.IWfDeployFormService;
import lombok.RequiredArgsConstructor;
import org.flowable.bpmn.model.BpmnModel;
import org.flowable.bpmn.model.FlowNode;
import org.flowable.bpmn.model.StartEvent;
import org.flowable.bpmn.model.UserTask;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
/**
* 流程实例关联表单Service业务层处理
*
* @author KonBAI
* @createTime 2022/3/7 22:07
*/
@RequiredArgsConstructor
@Service
public class WfDeployFormServiceImpl implements IWfDeployFormService {
private final WfDeployFormMapper baseMapper;
private final WfFormMapper formMapper;
/**
* 新增流程实例关联表单
*
* @param deployForm 流程实例关联表单
* @return 结果
*/
@Override
@Transactional(rollbackFor = Exception.class)
public int insertWfDeployForm(WfDeployForm deployForm) {
// 删除部署流程和表单的关联关系
baseMapper.delete(new LambdaQueryWrapper<WfDeployForm>().eq(WfDeployForm::getDeployId, deployForm.getDeployId()));
// 新增部署流程和表单关系
return baseMapper.insert(deployForm);
}
@Override
@Transactional(rollbackFor = Exception.class)
public boolean saveInternalDeployForm(String deployId, BpmnModel bpmnModel) {
List<WfDeployForm> deployFormList = new ArrayList<>();
// 获取开始节点
StartEvent startEvent = ModelUtils.getStartEvent(bpmnModel);
if (ObjectUtil.isNull(startEvent)) {
throw new RuntimeException("开始节点不存在,请检查流程设计是否有误!");
}
// 保存开始节点表单信息
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 baseMapper.insertBatch(deployFormList);
}
/**
* 查询流程挂着的表单
*
* @param deployId
* @return
*/
@Override
public WfFormVo selectDeployFormByDeployId(String deployId) {
QueryWrapper<WfForm> wrapper = Wrappers.query();
wrapper.eq("t2.deploy_id", deployId);
List<WfFormVo> list = formMapper.selectFormVoList(wrapper);
if (ObjectUtil.isNotEmpty(list)) {
if (list.size() != 1) {
throw new ServiceException("表单信息查询错误");
} else {
return list.get(0);
}
} else {
return null;
}
}
/**
* 构建部署表单关联信息对象
* @param deployId 部署ID
* @param node 节点信息
* @return 部署表单关联对象。若无表单信息formKey则返回null
*/
private WfDeployForm buildDeployForm(String deployId, FlowNode node) {
String formKey = ModelUtils.getFormKey(node);
if (StringUtils.isEmpty(formKey)) {
return null;
}
Long formId = Convert.toLong(StringUtils.substringAfter(formKey, "key_"));
WfForm wfForm = formMapper.selectById(formId);
if (ObjectUtil.isNull(wfForm)) {
throw new ServiceException("表单信息查询错误");
}
WfDeployForm deployForm = new WfDeployForm();
deployForm.setDeployId(deployId);
deployForm.setFormKey(formKey);
deployForm.setNodeKey(node.getId());
deployForm.setFormName(wfForm.getFormName());
deployForm.setNodeName(node.getName());
deployForm.setContent(wfForm.getContent());
return deployForm;
}
}

View File

@@ -1,146 +0,0 @@
package com.klp.workflow.service.impl;
import cn.hutool.core.io.IORuntimeException;
import cn.hutool.core.io.IoUtil;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.klp.common.core.domain.PageQuery;
import com.klp.common.core.page.TableDataInfo;
import com.klp.flowable.core.domain.ProcessQuery;
import com.klp.flowable.utils.ProcessUtils;
import com.klp.workflow.domain.WfDeployForm;
import com.klp.workflow.domain.vo.WfDeployVo;
import com.klp.workflow.mapper.WfDeployFormMapper;
import com.klp.workflow.service.IWfDeployService;
import lombok.RequiredArgsConstructor;
import org.flowable.common.engine.impl.db.SuspensionState;
import org.flowable.engine.RepositoryService;
import org.flowable.engine.repository.Deployment;
import org.flowable.engine.repository.ProcessDefinition;
import org.flowable.engine.repository.ProcessDefinitionQuery;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import java.io.InputStream;
import java.util.ArrayList;
import java.util.List;
import java.util.stream.Collectors;
/**
* @author KonBAI
* @createTime 2022/6/30 9:04
*/
@RequiredArgsConstructor
@Service
public class WfDeployServiceImpl implements IWfDeployService {
private final RepositoryService repositoryService;
private final WfDeployFormMapper deployFormMapper;
@Override
public TableDataInfo<WfDeployVo> queryPageList(ProcessQuery processQuery, PageQuery pageQuery) {
// 流程定义列表数据查询
ProcessDefinitionQuery processDefinitionQuery = repositoryService.createProcessDefinitionQuery()
.latestVersion()
.orderByProcessDefinitionKey()
.asc();
// 构建搜索条件
ProcessUtils.buildProcessSearch(processDefinitionQuery, processQuery);
long pageTotal = processDefinitionQuery.count();
if (pageTotal <= 0) {
return TableDataInfo.build();
}
int offset = pageQuery.getPageSize() * (pageQuery.getPageNum() - 1);
List<ProcessDefinition> definitionList = processDefinitionQuery.listPage(offset, pageQuery.getPageSize());
List<WfDeployVo> deployVoList = new ArrayList<>(definitionList.size());
for (ProcessDefinition processDefinition : definitionList) {
String deploymentId = processDefinition.getDeploymentId();
Deployment deployment = repositoryService.createDeploymentQuery().deploymentId(deploymentId).singleResult();
WfDeployVo vo = new WfDeployVo();
vo.setDefinitionId(processDefinition.getId());
vo.setProcessKey(processDefinition.getKey());
vo.setProcessName(processDefinition.getName());
vo.setVersion(processDefinition.getVersion());
vo.setCategory(processDefinition.getCategory());
vo.setDeploymentId(processDefinition.getDeploymentId());
vo.setSuspended(processDefinition.isSuspended());
// 流程部署信息
vo.setCategory(deployment.getCategory());
vo.setDeploymentTime(deployment.getDeploymentTime());
deployVoList.add(vo);
}
Page<WfDeployVo> page = new Page<>();
page.setRecords(deployVoList);
page.setTotal(pageTotal);
return TableDataInfo.build(page);
}
@Override
public TableDataInfo<WfDeployVo> queryPublishList(String processKey, PageQuery pageQuery) {
// 创建查询条件
ProcessDefinitionQuery processDefinitionQuery = repositoryService.createProcessDefinitionQuery()
.processDefinitionKey(processKey)
.orderByProcessDefinitionVersion()
.desc();
long pageTotal = processDefinitionQuery.count();
if (pageTotal <= 0) {
return TableDataInfo.build();
}
// 根据查询条件,查询所有版本
int offset = pageQuery.getPageSize() * (pageQuery.getPageNum() - 1);
List<ProcessDefinition> processDefinitionList = processDefinitionQuery
.listPage(offset, pageQuery.getPageSize());
List<WfDeployVo> deployVoList = processDefinitionList.stream().map(item -> {
WfDeployVo vo = new WfDeployVo();
vo.setDefinitionId(item.getId());
vo.setProcessKey(item.getKey());
vo.setProcessName(item.getName());
vo.setVersion(item.getVersion());
vo.setCategory(item.getCategory());
vo.setDeploymentId(item.getDeploymentId());
vo.setSuspended(item.isSuspended());
return vo;
}).collect(Collectors.toList());
Page<WfDeployVo> page = new Page<>();
page.setRecords(deployVoList);
page.setTotal(pageTotal);
return TableDataInfo.build(page);
}
/**
* 激活或挂起流程
*
* @param state 状态
* @param definitionId 流程定义ID
*/
@Override
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);
}
}
@Override
public String queryBpmnXmlById(String definitionId) {
InputStream inputStream = repositoryService.getProcessModel(definitionId);
try {
return IoUtil.readUtf8(inputStream);
} catch (IORuntimeException exception) {
throw new RuntimeException("加载xml文件异常");
}
}
@Override
@Transactional(rollbackFor = Exception.class)
public void deleteByIds(List<String> deployIds) {
for (String deployId : deployIds) {
repositoryService.deleteDeployment(deployId, true);
deployFormMapper.delete(new LambdaQueryWrapper<WfDeployForm>().eq(WfDeployForm::getDeployId, deployId));
}
}
}

View File

@@ -1,118 +0,0 @@
package com.klp.workflow.service.impl;
import cn.hutool.core.util.StrUtil;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.klp.common.core.domain.PageQuery;
import com.klp.common.core.page.TableDataInfo;
import com.klp.common.utils.StringUtils;
import com.klp.workflow.domain.WfForm;
import com.klp.workflow.domain.bo.WfFormBo;
import com.klp.workflow.domain.vo.WfFormVo;
import com.klp.workflow.mapper.WfFormMapper;
import com.klp.workflow.service.IWfFormService;
import lombok.RequiredArgsConstructor;
import org.springframework.stereotype.Service;
import java.util.Collection;
import java.util.List;
import java.util.Map;
/**
* 流程表单Service业务层处理
*
* @author KonBAI
* @createTime 2022/3/7 22:07
*/
@RequiredArgsConstructor
@Service
public class WfFormServiceImpl implements IWfFormService {
private final WfFormMapper baseMapper;
/**
* 查询流程表单
*
* @param formId 流程表单ID
* @return 流程表单
*/
@Override
public WfFormVo queryById(Long formId) {
return baseMapper.selectVoById(formId);
}
/**
* 查询流程表单列表
*
* @param bo 流程表单
* @return 流程表单
*/
@Override
public TableDataInfo<WfFormVo> queryPageList(WfFormBo bo, PageQuery pageQuery) {
LambdaQueryWrapper<WfForm> lqw = buildQueryWrapper(bo);
Page<WfFormVo> result = baseMapper.selectVoPage(pageQuery.build(), lqw);
return TableDataInfo.build(result);
}
/**
* 查询流程表单列表
*
* @param bo 流程表单
* @return 流程表单
*/
@Override
public List<WfFormVo> queryList(WfFormBo bo) {
LambdaQueryWrapper<WfForm> lqw = buildQueryWrapper(bo);
return baseMapper.selectVoList(lqw);
}
/**
* 新增流程表单
*
* @param bo 流程表单
* @return 结果
*/
@Override
public int insertForm(WfFormBo bo) {
WfForm wfForm = new WfForm();
wfForm.setFormName(bo.getFormName());
wfForm.setContent(bo.getContent());
wfForm.setRemark(bo.getRemark());
return baseMapper.insert(wfForm);
}
/**
* 修改流程表单
*
* @param bo 流程表单
* @return 结果
*/
@Override
public int updateForm(WfFormBo bo) {
return baseMapper.update(new WfForm(), new LambdaUpdateWrapper<WfForm>()
.set(StrUtil.isNotBlank(bo.getFormName()), WfForm::getFormName, bo.getFormName())
.set(StrUtil.isNotBlank(bo.getContent()), WfForm::getContent, bo.getContent())
.set(StrUtil.isNotBlank(bo.getRemark()), WfForm::getRemark, bo.getRemark())
.eq(WfForm::getFormId, bo.getFormId()));
}
/**
* 批量删除流程表单
*
* @param ids 需要删除的流程表单ID
* @return 结果
*/
@Override
public Boolean deleteWithValidByIds(Collection<Long> ids) {
return baseMapper.deleteBatchIds(ids) > 0;
}
private LambdaQueryWrapper<WfForm> buildQueryWrapper(WfFormBo bo) {
Map<String, Object> params = bo.getParams();
LambdaQueryWrapper<WfForm> lqw = Wrappers.lambdaQuery();
lqw.like(StringUtils.isNotBlank(bo.getFormName()), WfForm::getFormName, bo.getFormName());
return lqw;
}
}

View File

@@ -1,212 +0,0 @@
package com.klp.workflow.service.impl;
import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.date.BetweenFormatter;
import cn.hutool.core.date.DateUtil;
import cn.hutool.core.util.ObjectUtil;
import com.klp.common.core.domain.entity.SysDept;
import com.klp.common.core.domain.entity.SysRole;
import com.klp.common.core.service.UserService;
import com.klp.common.exception.ServiceException;
import com.klp.common.utils.JsonUtils;
import com.klp.common.utils.StringUtils;
import com.klp.flowable.common.constant.TaskConstants;
import com.klp.flowable.factory.FlowServiceFactory;
import com.klp.system.service.ISysDeptService;
import com.klp.system.service.ISysRoleService;
import com.klp.workflow.domain.bo.WfTaskBo;
import com.klp.workflow.domain.vo.WfFormVo;
import com.klp.workflow.domain.vo.WfTaskVo;
import com.klp.workflow.service.IWfDeployFormService;
import com.klp.workflow.service.IWfInstanceService;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.flowable.common.engine.api.FlowableObjectNotFoundException;
import org.flowable.engine.history.HistoricProcessInstance;
import org.flowable.engine.task.Comment;
import org.flowable.identitylink.api.history.HistoricIdentityLink;
import org.flowable.task.api.history.HistoricTaskInstance;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import java.util.*;
/**
* 工作流流程实例管理
*
* @author KonBAI
* @createTime 2022/3/10 00:12
*/
@RequiredArgsConstructor
@Service
@Slf4j
public class WfInstanceServiceImpl extends FlowServiceFactory implements IWfInstanceService {
private final IWfDeployFormService deployFormService;
private final UserService userService;
private final ISysRoleService roleService;
private final ISysDeptService deptService;
/**
* 结束流程实例
*
* @param vo
*/
@Override
public void stopProcessInstance(WfTaskBo vo) {
String taskId = vo.getTaskId();
}
/**
* 激活或挂起流程实例
*
* @param state 状态
* @param instanceId 流程实例ID
*/
@Override
public void updateState(Integer state, String instanceId) {
// 激活
if (state == 1) {
runtimeService.activateProcessInstanceById(instanceId);
}
// 挂起
if (state == 2) {
runtimeService.suspendProcessInstanceById(instanceId);
}
}
/**
* 删除流程实例ID
*
* @param instanceId 流程实例ID
* @param deleteReason 删除原因
*/
@Override
@Transactional(rollbackFor = Exception.class)
public void delete(String instanceId, String deleteReason) {
// 查询历史数据
HistoricProcessInstance historicProcessInstance = getHistoricProcessInstanceById(instanceId);
if (historicProcessInstance.getEndTime() != null) {
historyService.deleteHistoricProcessInstance(historicProcessInstance.getId());
return;
}
// 删除流程实例
runtimeService.deleteProcessInstance(instanceId, deleteReason);
// 删除历史流程实例
historyService.deleteHistoricProcessInstance(instanceId);
}
/**
* 根据实例ID查询历史实例数据
*
* @param processInstanceId
* @return
*/
@Override
public HistoricProcessInstance getHistoricProcessInstanceById(String processInstanceId) {
HistoricProcessInstance historicProcessInstance =
historyService.createHistoricProcessInstanceQuery().processInstanceId(processInstanceId).singleResult();
if (Objects.isNull(historicProcessInstance)) {
throw new FlowableObjectNotFoundException("流程实例不存在: " + processInstanceId);
}
return historicProcessInstance;
}
/**
* 流程历史流转记录
*
* @param procInsId 流程实例Id
* @return
*/
@Override
public Map<String, Object> queryDetailProcess(String procInsId, String deployId) {
Map<String, Object> map = new HashMap<>();
if (StringUtils.isNotBlank(procInsId)) {
List<HistoricTaskInstance> taskInstanceList = historyService.createHistoricTaskInstanceQuery()
.processInstanceId(procInsId)
.orderByHistoricTaskInstanceStartTime().desc()
.list();
List<Comment> commentList = taskService.getProcessInstanceComments(procInsId);
List<WfTaskVo> taskVoList = new ArrayList<>(taskInstanceList.size());
taskInstanceList.forEach(taskInstance -> {
WfTaskVo taskVo = new WfTaskVo();
taskVo.setProcDefId(taskInstance.getProcessDefinitionId());
taskVo.setTaskId(taskInstance.getId());
taskVo.setTaskDefKey(taskInstance.getTaskDefinitionKey());
taskVo.setTaskName(taskInstance.getName());
taskVo.setCreateTime(taskInstance.getStartTime());
taskVo.setFinishTime(taskInstance.getEndTime());
if (StringUtils.isNotBlank(taskInstance.getAssignee())) {
Long userId = Long.parseLong(taskInstance.getAssignee());
String nickName = userService.selectNickNameById(userId);
taskVo.setAssigneeId(userId);
taskVo.setAssigneeName(nickName);
}
// 展示审批人员
List<HistoricIdentityLink> linksForTask = historyService.getHistoricIdentityLinksForTask(taskInstance.getId());
StringBuilder stringBuilder = new StringBuilder();
for (HistoricIdentityLink identityLink : linksForTask) {
if ("candidate".equals(identityLink.getType())) {
if (StringUtils.isNotBlank(identityLink.getUserId())) {
Long userId = Long.parseLong(identityLink.getUserId());
String nickName = userService.selectNickNameById(userId);
stringBuilder.append(nickName).append(",");
}
if (StringUtils.isNotBlank(identityLink.getGroupId())) {
if (identityLink.getGroupId().startsWith(TaskConstants.ROLE_GROUP_PREFIX)) {
Long roleId = Long.parseLong(StringUtils.stripStart(identityLink.getGroupId(), TaskConstants.ROLE_GROUP_PREFIX));
SysRole role = roleService.selectRoleById(roleId);
stringBuilder.append(role.getRoleName()).append(",");
} else if (identityLink.getGroupId().startsWith(TaskConstants.DEPT_GROUP_PREFIX)) {
Long deptId = Long.parseLong(StringUtils.stripStart(identityLink.getGroupId(), TaskConstants.DEPT_GROUP_PREFIX));
SysDept dept = deptService.selectDeptById(deptId);
stringBuilder.append(dept.getDeptName()).append(",");
}
}
}
}
if (StringUtils.isNotBlank(stringBuilder)) {
taskVo.setCandidate(stringBuilder.substring(0, stringBuilder.length() - 1));
}
if (ObjectUtil.isNotNull(taskInstance.getDurationInMillis())) {
taskVo.setDuration(DateUtil.formatBetween(taskInstance.getDurationInMillis(), BetweenFormatter.Level.SECOND));
}
// 获取意见评论内容
if (CollUtil.isNotEmpty(commentList)) {
List<Comment> comments = new ArrayList<>();
// commentList.stream().filter(comment -> taskInstance.getId().equals(comment.getTaskId())).collect(Collectors.toList());
for (Comment comment : commentList) {
if (comment.getTaskId().equals(taskInstance.getId())) {
comments.add(comment);
// taskVo.setComment(WfCommentDto.builder().type(comment.getType()).comment(comment.getFullMessage()).build());
}
}
taskVo.setCommentList(comments);
}
taskVoList.add(taskVo);
});
map.put("flowList", taskVoList);
// // 查询当前任务是否完成
// List<Task> taskList = taskService.createTaskQuery().processInstanceId(procInsId).list();
// if (CollectionUtils.isNotEmpty(taskList)) {
// map.put("finished", true);
// } else {
// map.put("finished", false);
// }
}
// 第一次申请获取初始化表单
if (StringUtils.isNotBlank(deployId)) {
WfFormVo formVo = deployFormService.selectDeployFormByDeployId(deployId);
if (Objects.isNull(formVo)) {
throw new ServiceException("请先配置流程表单");
}
map.put("formData", JsonUtils.parseObject(formVo.getContent(), Map.class));
}
return map;
}
}

View File

@@ -1,359 +0,0 @@
package com.klp.workflow.service.impl;
import cn.hutool.core.util.ArrayUtil;
import cn.hutool.core.util.ObjectUtil;
import cn.hutool.core.util.StrUtil;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.klp.common.core.domain.PageQuery;
import com.klp.common.core.page.TableDataInfo;
import com.klp.common.helper.LoginHelper;
import com.klp.common.utils.JsonUtils;
import com.klp.common.utils.StringUtils;
import com.klp.flowable.common.constant.ProcessConstants;
import com.klp.flowable.common.enums.FormType;
import com.klp.flowable.factory.FlowServiceFactory;
import com.klp.flowable.utils.ModelUtils;
import com.klp.workflow.domain.bo.WfModelBo;
import com.klp.workflow.domain.dto.WfMetaInfoDto;
import com.klp.workflow.domain.vo.WfFormVo;
import com.klp.workflow.domain.vo.WfModelVo;
import com.klp.workflow.service.IWfDeployFormService;
import com.klp.workflow.service.IWfFormService;
import com.klp.workflow.service.IWfModelService;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.flowable.bpmn.model.BpmnModel;
import org.flowable.bpmn.model.StartEvent;
import org.flowable.engine.repository.Deployment;
import org.flowable.engine.repository.Model;
import org.flowable.engine.repository.ModelQuery;
import org.flowable.engine.repository.ProcessDefinition;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import java.nio.charset.StandardCharsets;
import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
/**
* @author KonBAI
* @createTime 2022/6/21 9:11
*/
@RequiredArgsConstructor
@Service
@Slf4j
public class WfModelServiceImpl extends FlowServiceFactory implements IWfModelService {
private final IWfFormService formService;
private final IWfDeployFormService deployFormService;
@Override
public TableDataInfo<WfModelVo> list(WfModelBo modelBo, PageQuery pageQuery) {
ModelQuery modelQuery = repositoryService.createModelQuery().latestVersion().orderByCreateTime().desc();
// 构建查询条件
if (StringUtils.isNotBlank(modelBo.getModelKey())) {
modelQuery.modelKey(modelBo.getModelKey());
}
if (StringUtils.isNotBlank(modelBo.getModelName())) {
modelQuery.modelNameLike("%" + modelBo.getModelName() + "%");
}
if (StringUtils.isNotBlank(modelBo.getCategory())) {
modelQuery.modelCategory(modelBo.getCategory());
}
// 执行查询
long pageTotal = modelQuery.count();
if (pageTotal <= 0) {
return TableDataInfo.build();
}
int offset = pageQuery.getPageSize() * (pageQuery.getPageNum() - 1);
List<Model> modelList = modelQuery.listPage(offset, pageQuery.getPageSize());
List<WfModelVo> modelVoList = new ArrayList<>(modelList.size());
modelList.forEach(model -> {
WfModelVo modelVo = new WfModelVo();
modelVo.setModelId(model.getId());
modelVo.setModelName(model.getName());
modelVo.setModelKey(model.getKey());
modelVo.setCategory(model.getCategory());
modelVo.setCreateTime(model.getCreateTime());
modelVo.setVersion(model.getVersion());
WfMetaInfoDto metaInfo = JsonUtils.parseObject(model.getMetaInfo(), WfMetaInfoDto.class);
if (metaInfo != null) {
modelVo.setDescription(metaInfo.getDescription());
modelVo.setFormType(metaInfo.getFormType());
modelVo.setFormId(metaInfo.getFormId());
}
modelVoList.add(modelVo);
});
Page<WfModelVo> page = new Page<>();
page.setRecords(modelVoList);
page.setTotal(pageTotal);
return TableDataInfo.build(page);
}
@Override
public List<WfModelVo> list(WfModelBo modelBo) {
ModelQuery modelQuery = repositoryService.createModelQuery().latestVersion().orderByCreateTime().desc();
// 构建查询条件
if (StringUtils.isNotBlank(modelBo.getModelKey())) {
modelQuery.modelKey(modelBo.getModelKey());
}
if (StringUtils.isNotBlank(modelBo.getModelName())) {
modelQuery.modelNameLike("%" + modelBo.getModelName() + "%");
}
if (StringUtils.isNotBlank(modelBo.getCategory())) {
modelQuery.modelCategory(modelBo.getCategory());
}
List<Model> modelList = modelQuery.list();
List<WfModelVo> modelVoList = new ArrayList<>(modelList.size());
modelList.forEach(model -> {
WfModelVo modelVo = new WfModelVo();
modelVo.setModelId(model.getId());
modelVo.setModelName(model.getName());
modelVo.setModelKey(model.getKey());
modelVo.setCategory(model.getCategory());
modelVo.setCreateTime(model.getCreateTime());
modelVo.setVersion(model.getVersion());
WfMetaInfoDto metaInfo = JsonUtils.parseObject(model.getMetaInfo(), WfMetaInfoDto.class);
if (metaInfo != null) {
modelVo.setDescription(metaInfo.getDescription());
modelVo.setFormType(metaInfo.getFormType());
modelVo.setFormId(metaInfo.getFormId());
}
modelVoList.add(modelVo);
});
return modelVoList;
}
@Override
public TableDataInfo<WfModelVo> historyList(WfModelBo modelBo, PageQuery pageQuery) {
ModelQuery modelQuery = repositoryService.createModelQuery()
.modelKey(modelBo.getModelKey())
.orderByModelVersion()
.desc();
// 执行查询(不显示最新版,-1
long pageTotal = modelQuery.count() - 1;
if (pageTotal <= 0) {
return TableDataInfo.build();
}
// offset+1去掉最新版
int offset = 1 + pageQuery.getPageSize() * (pageQuery.getPageNum() - 1);
List<Model> modelList = modelQuery.listPage(offset, pageQuery.getPageSize());
List<WfModelVo> modelVoList = new ArrayList<>(modelList.size());
modelList.forEach(model -> {
WfModelVo modelVo = new WfModelVo();
modelVo.setModelId(model.getId());
modelVo.setModelName(model.getName());
modelVo.setModelKey(model.getKey());
modelVo.setCategory(model.getCategory());
modelVo.setCreateTime(model.getCreateTime());
modelVo.setVersion(model.getVersion());
WfMetaInfoDto metaInfo = JsonUtils.parseObject(model.getMetaInfo(), WfMetaInfoDto.class);
if (metaInfo != null) {
modelVo.setDescription(metaInfo.getDescription());
modelVo.setFormType(metaInfo.getFormType());
modelVo.setFormId(metaInfo.getFormId());
}
modelVoList.add(modelVo);
});
Page<WfModelVo> page = new Page<>();
page.setRecords(modelVoList);
page.setTotal(pageTotal);
return TableDataInfo.build(page);
}
@Override
public WfModelVo getModel(String modelId) {
// 获取流程模型
Model model = repositoryService.getModel(modelId);
if (ObjectUtil.isNull(model)) {
throw new RuntimeException("流程模型不存在!");
}
// 获取流程图
String bpmnXml = queryBpmnXmlById(modelId);
WfModelVo modelVo = new WfModelVo();
modelVo.setModelId(model.getId());
modelVo.setModelName(model.getName());
modelVo.setModelKey(model.getKey());
modelVo.setCategory(model.getCategory());
modelVo.setCreateTime(model.getCreateTime());
modelVo.setVersion(model.getVersion());
modelVo.setBpmnXml(bpmnXml);
WfMetaInfoDto metaInfo = JsonUtils.parseObject(model.getMetaInfo(), WfMetaInfoDto.class);
if (metaInfo != null) {
modelVo.setDescription(metaInfo.getDescription());
modelVo.setFormType(metaInfo.getFormType());
modelVo.setFormId(metaInfo.getFormId());
if (FormType.PROCESS.getType().equals(metaInfo.getFormType())) {
WfFormVo wfFormVo = formService.queryById(metaInfo.getFormId());
modelVo.setContent(wfFormVo.getContent());
}
}
return modelVo;
}
@Override
public String queryBpmnXmlById(String modelId) {
byte[] bpmnBytes = repositoryService.getModelEditorSource(modelId);
return StrUtil.utf8Str(bpmnBytes);
}
@Override
public void insertModel(WfModelBo modelBo) {
Model model = repositoryService.newModel();
model.setName(modelBo.getModelName());
model.setKey(modelBo.getModelKey());
model.setCategory(modelBo.getCategory());
String metaInfo = buildMetaInfo(new WfMetaInfoDto(), modelBo.getDescription());
model.setMetaInfo(metaInfo);
// 保存流程模型
repositoryService.saveModel(model);
}
@Override
@Transactional(rollbackFor = Exception.class)
public void updateModel(WfModelBo modelBo) {
// 根据模型Key查询模型信息
Model model = repositoryService.getModel(modelBo.getModelId());
if (ObjectUtil.isNull(model)) {
throw new RuntimeException("流程模型不存在!");
}
model.setCategory(modelBo.getCategory());
WfMetaInfoDto metaInfoDto = JsonUtils.parseObject(model.getMetaInfo(), WfMetaInfoDto.class);
String metaInfo = buildMetaInfo(metaInfoDto, modelBo.getDescription());
model.setMetaInfo(metaInfo);
// 保存流程模型
repositoryService.saveModel(model);
}
@Override
@Transactional(rollbackFor = Exception.class)
public void saveModel(WfModelBo modelBo) {
// 查询模型信息
Model model = repositoryService.getModel(modelBo.getModelId());
if (ObjectUtil.isNull(model)) {
throw new RuntimeException("流程模型不存在!");
}
BpmnModel bpmnModel = ModelUtils.getBpmnModel(modelBo.getBpmnXml());
if (ObjectUtil.isEmpty(bpmnModel)) {
throw new RuntimeException("获取模型设计失败!");
}
String processName = bpmnModel.getMainProcess().getName();
// 获取开始节点
StartEvent startEvent = ModelUtils.getStartEvent(bpmnModel);
if (ObjectUtil.isNull(startEvent)) {
throw new RuntimeException("开始节点不存在,请检查流程设计是否有误!");
}
// 获取开始节点配置的表单Key
if (StrUtil.isBlank(startEvent.getFormKey())) {
throw new RuntimeException("请配置流程表单");
}
Model newModel;
if (Boolean.TRUE.equals(modelBo.getNewVersion())) {
newModel = repositoryService.newModel();
newModel.setName(processName);
newModel.setKey(model.getKey());
newModel.setCategory(model.getCategory());
newModel.setMetaInfo(model.getMetaInfo());
newModel.setVersion(model.getVersion() + 1);
} else {
newModel = model;
// 设置流程名称
newModel.setName(processName);
}
// 保存流程模型
repositoryService.saveModel(newModel);
// 保存 BPMN XML
byte[] bpmnXmlBytes = StringUtils.getBytes(modelBo.getBpmnXml(), StandardCharsets.UTF_8);
repositoryService.addModelEditorSource(newModel.getId(), bpmnXmlBytes);
}
@Override
@Transactional(rollbackFor = Exception.class)
public void latestModel(String modelId) {
// 获取流程模型
Model model = repositoryService.getModel(modelId);
if (ObjectUtil.isNull(model)) {
throw new RuntimeException("流程模型不存在!");
}
Integer latestVersion = repositoryService.createModelQuery()
.modelKey(model.getKey())
.latestVersion()
.singleResult()
.getVersion();
if (model.getVersion().equals(latestVersion)) {
throw new RuntimeException("当前版本已是最新版!");
}
// 获取 BPMN XML
byte[] bpmnBytes = repositoryService.getModelEditorSource(modelId);
Model newModel = repositoryService.newModel();
newModel.setName(model.getName());
newModel.setKey(model.getKey());
newModel.setCategory(model.getCategory());
newModel.setMetaInfo(model.getMetaInfo());
newModel.setVersion(latestVersion + 1);
// 保存流程模型
repositoryService.saveModel(newModel);
// 保存 BPMN XML
repositoryService.addModelEditorSource(newModel.getId(), bpmnBytes);
}
@Override
@Transactional(rollbackFor = Exception.class)
public void deleteByIds(Collection<String> ids) {
ids.forEach(id -> {
Model model = repositoryService.getModel(id);
if (ObjectUtil.isNull(model)) {
throw new RuntimeException("流程模型不存在!");
}
repositoryService.deleteModel(id);
});
}
@Override
@Transactional(rollbackFor = Exception.class)
public boolean deployModel(String modelId) {
// 获取流程模型
Model model = repositoryService.getModel(modelId);
if (ObjectUtil.isNull(model)) {
throw new RuntimeException("流程模型不存在!");
}
// 获取流程图
byte[] bpmnBytes = repositoryService.getModelEditorSource(modelId);
if (ArrayUtil.isEmpty(bpmnBytes)) {
throw new RuntimeException("请先设计流程图!");
}
String bpmnXml = StringUtils.toEncodedString(bpmnBytes, StandardCharsets.UTF_8);
BpmnModel bpmnModel = ModelUtils.getBpmnModel(bpmnXml);
String processName = model.getName() + ProcessConstants.SUFFIX;
// 部署流程
Deployment deployment = repositoryService.createDeployment()
.name(model.getName())
.key(model.getKey())
.category(model.getCategory())
.addBytes(processName, bpmnBytes)
.deploy();
ProcessDefinition procDef = repositoryService.createProcessDefinitionQuery()
.deploymentId(deployment.getId())
.singleResult();
// 修改流程定义的分类,便于搜索流程
repositoryService.setProcessDefinitionCategory(procDef.getId(), model.getCategory());
// 保存部署表单
return deployFormService.saveInternalDeployForm(deployment.getId(), bpmnModel);
}
/**
* 构建模型扩展信息
* @return
*/
private String buildMetaInfo(WfMetaInfoDto metaInfo, String description) {
// 只有非空,才进行设置,避免更新时的覆盖
if (StringUtils.isNotEmpty(description)) {
metaInfo.setDescription(description);
}
if (StringUtils.isNotEmpty(metaInfo.getCreateUser())) {
metaInfo.setCreateUser(LoginHelper.getUsername());
}
return JsonUtils.toJsonString(metaInfo);
}
}

View File

@@ -1,625 +0,0 @@
package com.klp.workflow.service.impl;
import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.convert.Convert;
import cn.hutool.core.util.ObjectUtil;
import cn.hutool.core.util.StrUtil;
import com.klp.common.core.service.UserService;
import com.klp.common.exception.ServiceException;
import com.klp.common.helper.LoginHelper;
import com.klp.common.utils.StringUtils;
import com.klp.flowable.common.constant.ProcessConstants;
import com.klp.flowable.common.constant.TaskConstants;
import com.klp.flowable.common.enums.FlowComment;
import com.klp.flowable.common.enums.ProcessStatus;
import com.klp.flowable.factory.FlowServiceFactory;
import com.klp.flowable.flow.CustomProcessDiagramGenerator;
import com.klp.flowable.flow.FlowableUtils;
import com.klp.flowable.utils.ModelUtils;
import com.klp.flowable.utils.TaskUtils;
import com.klp.workflow.domain.bo.WfTaskBo;
import com.klp.workflow.service.IWfCopyService;
import com.klp.workflow.service.IWfTaskService;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.collections4.CollectionUtils;
import org.flowable.bpmn.constants.BpmnXMLConstants;
import org.flowable.bpmn.model.Process;
import org.flowable.bpmn.model.*;
import org.flowable.common.engine.api.FlowableException;
import org.flowable.common.engine.api.FlowableObjectNotFoundException;
import org.flowable.common.engine.impl.identity.Authentication;
import org.flowable.engine.ProcessEngineConfiguration;
import org.flowable.engine.history.HistoricActivityInstance;
import org.flowable.engine.history.HistoricProcessInstance;
import org.flowable.engine.repository.ProcessDefinition;
import org.flowable.engine.runtime.Execution;
import org.flowable.engine.runtime.ProcessInstance;
import org.flowable.image.ProcessDiagramGenerator;
import org.flowable.task.api.DelegationState;
import org.flowable.task.api.Task;
import org.flowable.task.api.history.HistoricTaskInstance;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import java.io.InputStream;
import java.util.*;
import java.util.stream.Collectors;
/**
* @author KonBAI
* @createTime 2022/3/10 00:12
*/
@RequiredArgsConstructor
@Service
@Slf4j
public class WfTaskServiceImpl extends FlowServiceFactory implements IWfTaskService {
private final UserService sysUserService;
private final IWfCopyService copyService;
/**
* 完成任务
*
* @param taskBo 请求实体参数
*/
@Transactional(rollbackFor = Exception.class)
@Override
public void complete(WfTaskBo taskBo) {
Task task = taskService.createTaskQuery().taskId(taskBo.getTaskId()).singleResult();
if (Objects.isNull(task)) {
throw new ServiceException("任务不存在");
}
// 获取 bpmn 模型
BpmnModel bpmnModel = repositoryService.getBpmnModel(task.getProcessDefinitionId());
identityService.setAuthenticatedUserId(TaskUtils.getUserId());
if (DelegationState.PENDING.equals(task.getDelegationState())) {
taskService.addComment(taskBo.getTaskId(), taskBo.getProcInsId(), FlowComment.DELEGATE.getType(), taskBo.getComment());
taskService.resolveTask(taskBo.getTaskId());
} else {
taskService.addComment(taskBo.getTaskId(), taskBo.getProcInsId(), FlowComment.NORMAL.getType(), taskBo.getComment());
taskService.setAssignee(taskBo.getTaskId(), TaskUtils.getUserId());
if (ObjectUtil.isNotEmpty(taskBo.getVariables())) {
// 获取模型信息
String localScopeValue = ModelUtils.getUserTaskAttributeValue(bpmnModel, task.getTaskDefinitionKey(), ProcessConstants.PROCESS_FORM_LOCAL_SCOPE);
boolean localScope = Convert.toBool(localScopeValue, false);
taskService.complete(taskBo.getTaskId(), taskBo.getVariables(), localScope);
} else {
taskService.complete(taskBo.getTaskId());
}
}
// 设置任务节点名称
taskBo.setTaskName(task.getName());
// 处理下一级审批人
if (StringUtils.isNotBlank(taskBo.getNextUserIds())) {
this.assignNextUsers(bpmnModel, taskBo.getProcInsId(), taskBo.getNextUserIds());
}
// 处理抄送用户
if (!copyService.makeCopy(taskBo)) {
throw new RuntimeException("抄送任务失败");
}
}
/**
* 拒绝任务
*
* @param taskBo
*/
@Override
@Transactional(rollbackFor = Exception.class)
public void taskReject(WfTaskBo taskBo) {
// 当前任务 task
Task task = taskService.createTaskQuery().taskId(taskBo.getTaskId()).singleResult();
if (ObjectUtil.isNull(task)) {
throw new RuntimeException("获取任务信息异常!");
}
if (task.isSuspended()) {
throw new RuntimeException("任务处于挂起状态");
}
// 获取流程实例
ProcessInstance processInstance = runtimeService.createProcessInstanceQuery()
.processInstanceId(taskBo.getProcInsId())
.singleResult();
if (processInstance == null) {
throw new RuntimeException("流程实例不存在,请确认!");
}
// 获取流程定义信息
ProcessDefinition processDefinition = repositoryService.createProcessDefinitionQuery()
.processDefinitionId(task.getProcessDefinitionId())
.singleResult();
identityService.setAuthenticatedUserId(TaskUtils.getUserId());
// 添加审批意见
taskService.addComment(taskBo.getTaskId(), taskBo.getProcInsId(), FlowComment.REJECT.getType(), taskBo.getComment());
// 设置流程状态为已终结
runtimeService.setVariable(processInstance.getId(), ProcessConstants.PROCESS_STATUS_KEY, ProcessStatus.TERMINATED.getStatus());
// 获取所有节点信息
BpmnModel bpmnModel = repositoryService.getBpmnModel(processDefinition.getId());
EndEvent endEvent = ModelUtils.getEndEvent(bpmnModel);
// 终止流程
List<Execution> executions = runtimeService.createExecutionQuery().parentId(task.getProcessInstanceId()).list();
List<String> executionIds = executions.stream().map(Execution::getId).collect(Collectors.toList());
runtimeService.createChangeActivityStateBuilder()
.processInstanceId(task.getProcessInstanceId())
.moveExecutionsToSingleActivityId(executionIds, endEvent.getId())
.changeState();
// 处理抄送用户
if (!copyService.makeCopy(taskBo)) {
throw new RuntimeException("抄送任务失败");
}
}
/**
* 退回任务
*
* @param bo 请求实体参数
*/
@Transactional(rollbackFor = Exception.class)
@Override
public void taskReturn(WfTaskBo bo) {
// 当前任务 task
Task task = taskService.createTaskQuery().taskId(bo.getTaskId()).singleResult();
if (ObjectUtil.isNull(task)) {
throw new RuntimeException("获取任务信息异常!");
}
if (task.isSuspended()) {
throw new RuntimeException("任务处于挂起状态");
}
// 获取流程定义信息
ProcessDefinition processDefinition = repositoryService.createProcessDefinitionQuery().processDefinitionId(task.getProcessDefinitionId()).singleResult();
// 获取流程模型信息
BpmnModel bpmnModel = repositoryService.getBpmnModel(processDefinition.getId());
// 获取当前任务节点元素
FlowElement source = ModelUtils.getFlowElementById(bpmnModel, task.getTaskDefinitionKey());
// 获取跳转的节点元素
FlowElement target = ModelUtils.getFlowElementById(bpmnModel, bo.getTargetKey());
// 从当前节点向前扫描,判断当前节点与目标节点是否属于串行,若目标节点是在并行网关上或非同一路线上,不可跳转
boolean isSequential = ModelUtils.isSequentialReachable(source, target, new HashSet<>());
if (!isSequential) {
throw new RuntimeException("当前节点相对于目标节点,不属于串行关系,无法回退");
}
// 获取所有正常进行的任务节点 Key这些任务不能直接使用需要找出其中需要撤回的任务
List<Task> runTaskList = taskService.createTaskQuery().processInstanceId(task.getProcessInstanceId()).list();
List<String> runTaskKeyList = new ArrayList<>();
runTaskList.forEach(item -> runTaskKeyList.add(item.getTaskDefinitionKey()));
// 需退回任务列表
List<String> currentIds = new ArrayList<>();
// 通过父级网关的出口连线,结合 runTaskList 比对,获取需要撤回的任务
List<UserTask> currentUserTaskList = FlowableUtils.iteratorFindChildUserTasks(target, runTaskKeyList, null, null);
currentUserTaskList.forEach(item -> currentIds.add(item.getId()));
// 循环获取那些需要被撤回的节点的ID用来设置驳回原因
List<String> currentTaskIds = new ArrayList<>();
currentIds.forEach(currentId -> runTaskList.forEach(runTask -> {
if (currentId.equals(runTask.getTaskDefinitionKey())) {
currentTaskIds.add(runTask.getId());
}
}));
identityService.setAuthenticatedUserId(TaskUtils.getUserId());
// 设置回退意见
for (String currentTaskId : currentTaskIds) {
taskService.addComment(currentTaskId, task.getProcessInstanceId(), FlowComment.REBACK.getType(), bo.getComment());
}
try {
// 1 对 1 或 多 对 1 情况currentIds 当前要跳转的节点列表(1或多)targetKey 跳转到的节点(1)
runtimeService.createChangeActivityStateBuilder()
.processInstanceId(task.getProcessInstanceId())
.moveActivityIdsToSingleActivityId(currentIds, bo.getTargetKey()).changeState();
} catch (FlowableObjectNotFoundException e) {
throw new RuntimeException("未找到流程实例,流程可能已发生变化");
} catch (FlowableException e) {
throw new RuntimeException("无法取消或开始活动");
}
// 设置任务节点名称
bo.setTaskName(task.getName());
// 处理抄送用户
if (!copyService.makeCopy(bo)) {
throw new RuntimeException("抄送任务失败");
}
}
/**
* 获取所有可回退的节点
*
* @param bo
* @return
*/
@Override
public List<FlowElement> findReturnTaskList(WfTaskBo bo) {
// 当前任务 task
Task task = taskService.createTaskQuery().taskId(bo.getTaskId()).singleResult();
// 获取流程定义信息
ProcessDefinition processDefinition = repositoryService.createProcessDefinitionQuery().processDefinitionId(task.getProcessDefinitionId()).singleResult();
// 获取流程模型信息
BpmnModel bpmnModel = repositoryService.getBpmnModel(processDefinition.getId());
// 查询历史节点实例
List<HistoricActivityInstance> activityInstanceList = historyService.createHistoricActivityInstanceQuery()
.processInstanceId(task.getProcessInstanceId())
.activityType(BpmnXMLConstants.ELEMENT_TASK_USER)
.finished()
.orderByHistoricActivityInstanceEndTime().asc()
.list();
List<String> activityIdList = activityInstanceList.stream()
.map(HistoricActivityInstance::getActivityId)
.filter(activityId -> !StringUtils.equals(activityId, task.getTaskDefinitionKey()))
.distinct()
.collect(Collectors.toList());
// 获取当前任务节点元素
FlowElement source = ModelUtils.getFlowElementById(bpmnModel, task.getTaskDefinitionKey());
List<FlowElement> elementList = new ArrayList<>();
for (String activityId : activityIdList) {
FlowElement target = ModelUtils.getFlowElementById(bpmnModel, activityId);
boolean isSequential = ModelUtils.isSequentialReachable(source, target, new HashSet<>());
if (isSequential) {
elementList.add(target);
}
}
return elementList;
}
/**
* 删除任务
*
* @param bo 请求实体参数
*/
@Override
public void deleteTask(WfTaskBo bo) {
// todo 待确认删除任务是物理删除任务 还是逻辑删除,让这个任务直接通过?
identityService.setAuthenticatedUserId(TaskUtils.getUserId());
taskService.deleteTask(bo.getTaskId(), bo.getComment());
}
/**
* 认领/签收任务
*
* @param taskBo 请求实体参数
*/
@Override
@Transactional(rollbackFor = Exception.class)
public void claim(WfTaskBo taskBo) {
Task task = taskService.createTaskQuery().taskId(taskBo.getTaskId()).singleResult();
if (Objects.isNull(task)) {
throw new ServiceException("任务不存在");
}
taskService.claim(taskBo.getTaskId(), TaskUtils.getUserId());
}
/**
* 取消认领/签收任务
*
* @param bo 请求实体参数
*/
@Override
@Transactional(rollbackFor = Exception.class)
public void unClaim(WfTaskBo bo) {
taskService.unclaim(bo.getTaskId());
}
/**
* 委派任务
*
* @param bo 请求实体参数
*/
@Override
@Transactional(rollbackFor = Exception.class)
public void delegateTask(WfTaskBo bo) {
// 当前任务 task
Task task = taskService.createTaskQuery().taskId(bo.getTaskId()).singleResult();
if (ObjectUtil.isEmpty(task)) {
throw new ServiceException("获取任务失败!");
}
StringBuilder commentBuilder = new StringBuilder(LoginHelper.getNickName())
.append("->");
String nickName = sysUserService.selectNickNameById(Long.parseLong(bo.getUserId()));
if (StringUtils.isNotBlank(nickName)) {
commentBuilder.append(nickName);
} else {
commentBuilder.append(bo.getUserId());
}
if (StringUtils.isNotBlank(bo.getComment())) {
commentBuilder.append(": ").append(bo.getComment());
}
identityService.setAuthenticatedUserId(TaskUtils.getUserId());
// 添加审批意见
taskService.addComment(bo.getTaskId(), task.getProcessInstanceId(), FlowComment.DELEGATE.getType(), commentBuilder.toString());
// 设置办理人为当前登录人
taskService.setOwner(bo.getTaskId(), TaskUtils.getUserId());
// 执行委派
taskService.delegateTask(bo.getTaskId(), bo.getUserId());
// 设置任务节点名称
bo.setTaskName(task.getName());
// 处理抄送用户
if (!copyService.makeCopy(bo)) {
throw new RuntimeException("抄送任务失败");
}
}
/**
* 转办任务
*
* @param bo 请求实体参数
*/
@Override
@Transactional(rollbackFor = Exception.class)
public void transferTask(WfTaskBo bo) {
// 当前任务 task
Task task = taskService.createTaskQuery().taskId(bo.getTaskId()).singleResult();
if (ObjectUtil.isEmpty(task)) {
throw new ServiceException("获取任务失败!");
}
StringBuilder commentBuilder = new StringBuilder(LoginHelper.getNickName())
.append("->");
String nickName = sysUserService.selectNickNameById(Long.parseLong(bo.getUserId()));
if (StringUtils.isNotBlank(nickName)) {
commentBuilder.append(nickName);
} else {
commentBuilder.append(bo.getUserId());
}
if (StringUtils.isNotBlank(bo.getComment())) {
commentBuilder.append(": ").append(bo.getComment());
}
identityService.setAuthenticatedUserId(TaskUtils.getUserId());
// 添加审批意见
taskService.addComment(bo.getTaskId(), task.getProcessInstanceId(), FlowComment.TRANSFER.getType(), commentBuilder.toString());
// 设置拥有者为当前登录人
taskService.setOwner(bo.getTaskId(), TaskUtils.getUserId());
// 转办任务
taskService.setAssignee(bo.getTaskId(), bo.getUserId());
// 设置任务节点名称
bo.setTaskName(task.getName());
// 处理抄送用户
if (!copyService.makeCopy(bo)) {
throw new RuntimeException("抄送任务失败");
}
}
/**
* 取消申请
*
* @param bo
* @return
*/
@Override
public void stopProcess(WfTaskBo bo) {
List<Task> taskList = taskService.createTaskQuery().processInstanceId(bo.getProcInsId()).list();
if (CollectionUtils.isEmpty(taskList)) {
throw new RuntimeException("流程未启动或已执行完成,取消申请失败");
}
ProcessInstance processInstance = runtimeService.createProcessInstanceQuery()
.processInstanceId(bo.getProcInsId()).singleResult();
BpmnModel bpmnModel = repositoryService.getBpmnModel(processInstance.getProcessDefinitionId());
if (Objects.nonNull(bpmnModel)) {
Process process = bpmnModel.getMainProcess();
List<EndEvent> endNodes = process.findFlowElementsOfType(EndEvent.class, false);
if (CollectionUtils.isNotEmpty(endNodes)) {
Authentication.setAuthenticatedUserId(TaskUtils.getUserId());
runtimeService.setVariable(processInstance.getId(), ProcessConstants.PROCESS_STATUS_KEY, ProcessStatus.CANCELED.getStatus());
for (Task task : taskList) {
taskService.addComment(task.getId(), processInstance.getProcessInstanceId(), FlowComment.STOP.getType(), "取消流程");
}
// 获取当前流程最后一个节点
String endId = endNodes.get(0).getId();
List<Execution> executions = runtimeService.createExecutionQuery()
.parentId(processInstance.getProcessInstanceId()).list();
List<String> executionIds = new ArrayList<>();
executions.forEach(execution -> executionIds.add(execution.getId()));
// 变更流程为已结束状态
runtimeService.createChangeActivityStateBuilder()
.moveExecutionsToSingleActivityId(executionIds, endId).changeState();
}
}
}
/**
* 撤回流程
*
* @param taskBo 请求实体参数
*/
@Override
@Transactional(rollbackFor = Exception.class)
public void revokeProcess(WfTaskBo taskBo) {
String procInsId = taskBo.getProcInsId();
String taskId = taskBo.getTaskId();
// 校验流程是否结束
ProcessInstance processInstance = runtimeService.createProcessInstanceQuery()
.processInstanceId(procInsId)
.active()
.singleResult();
if(ObjectUtil.isNull(processInstance)) {
throw new RuntimeException("流程已结束或已挂起,无法执行撤回操作");
}
// 获取待撤回任务实例
HistoricTaskInstance currTaskIns = historyService.createHistoricTaskInstanceQuery()
.taskId(taskId)
.taskAssignee(TaskUtils.getUserId())
.singleResult();
if (ObjectUtil.isNull(currTaskIns)) {
throw new RuntimeException("当前任务不存在,无法执行撤回操作");
}
// 获取 bpmn 模型
BpmnModel bpmnModel = repositoryService.getBpmnModel(currTaskIns.getProcessDefinitionId());
UserTask currUserTask = ModelUtils.getUserTaskByKey(bpmnModel, currTaskIns.getTaskDefinitionKey());
// 查找下一级用户任务列表
List<UserTask> nextUserTaskList = ModelUtils.findNextUserTasks(currUserTask);
List<String> nextUserTaskKeys = nextUserTaskList.stream().map(UserTask::getId).collect(Collectors.toList());
// 获取当前节点之后已完成的流程历史节点
List<HistoricTaskInstance> finishedTaskInsList = historyService.createHistoricTaskInstanceQuery()
.processInstanceId(procInsId)
.taskCreatedAfter(currTaskIns.getEndTime())
.finished()
.list();
for (HistoricTaskInstance finishedTaskInstance : finishedTaskInsList) {
// 检查已完成流程历史节点是否存在下一级中
if (CollUtil.contains(nextUserTaskKeys, finishedTaskInstance.getTaskDefinitionKey())) {
throw new RuntimeException("下一流程已处理,无法执行撤回操作");
}
}
// 获取所有激活的任务节点,找到需要撤回的任务
List<Task> activateTaskList = taskService.createTaskQuery().processInstanceId(procInsId).list();
List<String> revokeExecutionIds = new ArrayList<>();
identityService.setAuthenticatedUserId(TaskUtils.getUserId());
for (Task task : activateTaskList) {
// 检查激活的任务节点是否存在下一级中,如果存在,则加入到需要撤回的节点
if (CollUtil.contains(nextUserTaskKeys, task.getTaskDefinitionKey())) {
// 添加撤回审批信息
taskService.setAssignee(task.getId(), TaskUtils.getUserId());
taskService.addComment(task.getId(), task.getProcessInstanceId(), FlowComment.REVOKE.getType(), LoginHelper.getNickName() + "撤回流程审批");
revokeExecutionIds.add(task.getExecutionId());
}
}
try {
runtimeService.createChangeActivityStateBuilder()
.processInstanceId(procInsId)
.moveExecutionsToSingleActivityId(revokeExecutionIds, currTaskIns.getTaskDefinitionKey()).changeState();
} catch (FlowableObjectNotFoundException e) {
throw new RuntimeException("未找到流程实例,流程可能已发生变化");
} catch (FlowableException e) {
throw new RuntimeException("执行撤回操作失败");
}
}
/**
* 获取流程过程图
*
* @param processId
* @return
*/
@Override
public InputStream diagram(String processId) {
String processDefinitionId;
// 获取当前的流程实例
ProcessInstance processInstance = runtimeService.createProcessInstanceQuery().processInstanceId(processId).singleResult();
// 如果流程已经结束,则得到结束节点
if (Objects.isNull(processInstance)) {
HistoricProcessInstance pi = historyService.createHistoricProcessInstanceQuery().processInstanceId(processId).singleResult();
processDefinitionId = pi.getProcessDefinitionId();
} else {// 如果流程没有结束,则取当前活动节点
// 根据流程实例ID获得当前处于活动状态的ActivityId合集
ProcessInstance pi = runtimeService.createProcessInstanceQuery().processInstanceId(processId).singleResult();
processDefinitionId = pi.getProcessDefinitionId();
}
// 获得活动的节点
List<HistoricActivityInstance> highLightedFlowList = historyService.createHistoricActivityInstanceQuery()
.processInstanceId(processId).orderByHistoricActivityInstanceStartTime().asc().list();
List<String> highLightedFlows = new ArrayList<>();
List<String> highLightedNodes = new ArrayList<>();
//高亮线
for (HistoricActivityInstance tempActivity : highLightedFlowList) {
if ("sequenceFlow".equals(tempActivity.getActivityType())) {
//高亮线
highLightedFlows.add(tempActivity.getActivityId());
} else {
//高亮节点
highLightedNodes.add(tempActivity.getActivityId());
}
}
//获取流程图
BpmnModel bpmnModel = repositoryService.getBpmnModel(processDefinitionId);
ProcessEngineConfiguration configuration = processEngine.getProcessEngineConfiguration();
//获取自定义图片生成器
ProcessDiagramGenerator diagramGenerator = new CustomProcessDiagramGenerator();
return diagramGenerator.generateDiagram(bpmnModel, "png", highLightedNodes, highLightedFlows, configuration.getActivityFontName(),
configuration.getLabelFontName(), configuration.getAnnotationFontName(), configuration.getClassLoader(), 1.0, true);
}
/**
* 获取流程变量
*
* @param taskId 任务ID
* @return 流程变量
*/
@Override
public Map<String, Object> getProcessVariables(String taskId) {
HistoricTaskInstance historicTaskInstance = historyService.createHistoricTaskInstanceQuery()
.includeProcessVariables()
.finished()
.taskId(taskId)
.singleResult();
if (Objects.nonNull(historicTaskInstance)) {
return historicTaskInstance.getProcessVariables();
}
return taskService.getVariables(taskId);
}
/**
* 启动第一个任务
* @param processInstance 流程实例
* @param variables 流程参数
*/
@Override
public void startFirstTask(ProcessInstance processInstance, Map<String, Object> variables) {
// 若第一个用户任务为发起人,则自动完成任务
List<Task> tasks = taskService.createTaskQuery().processInstanceId(processInstance.getProcessInstanceId()).list();
if (CollUtil.isNotEmpty(tasks)) {
String userIdStr = (String) variables.get(TaskConstants.PROCESS_INITIATOR);
identityService.setAuthenticatedUserId(TaskUtils.getUserId());
for (Task task : tasks) {
if (StrUtil.equals(task.getAssignee(), userIdStr)) {
taskService.addComment(task.getId(), processInstance.getProcessInstanceId(), FlowComment.NORMAL.getType(), LoginHelper.getNickName() + "发起流程申请");
taskService.complete(task.getId(), variables);
}
}
}
}
/**
* 指派下一任务审批人
* @param bpmnModel bpmn模型
* @param processInsId 流程实例id
* @param userIds 用户ids
*/
private void assignNextUsers(BpmnModel bpmnModel, String processInsId, String userIds) {
// 获取所有节点信息
List<Task> list = taskService.createTaskQuery()
.processInstanceId(processInsId)
.list();
if (list.size() == 0) {
return;
}
Queue<String> assignIds = CollUtil.newLinkedList(userIds.split(","));
if (list.size() == assignIds.size()) {
for (Task task : list) {
taskService.setAssignee(task.getId(), assignIds.poll());
}
return;
}
// 优先处理非多实例任务
Iterator<Task> iterator = list.iterator();
while (iterator.hasNext()) {
Task task = iterator.next();
if (!ModelUtils.isMultiInstance(bpmnModel, task.getTaskDefinitionKey())) {
if (!assignIds.isEmpty()) {
taskService.setAssignee(task.getId(), assignIds.poll());
}
iterator.remove();
}
}
// 若存在多实例任务,则进行动态加减签
if (CollUtil.isNotEmpty(list)) {
if (assignIds.isEmpty()) {
// 动态减签
for (Task task : list) {
runtimeService.deleteMultiInstanceExecution(task.getExecutionId(), true);
}
} else {
// 动态加签
for (String assignId : assignIds) {
Map<String, Object> assignVariables = Collections.singletonMap(BpmnXMLConstants.ATTRIBUTE_TASK_USER_ASSIGNEE, assignId);
runtimeService.addMultiInstanceExecution(list.get(0).getTaskDefinitionKey(), list.get(0).getProcessInstanceId(), assignVariables);
}
}
}
}
}

View File

@@ -1,19 +0,0 @@
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper
PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.klp.workflow.mapper.WfCategoryMapper">
<resultMap type="com.klp.workflow.domain.WfCategory" id="FlowCategoryResult">
<result property="categoryId" column="category_id"/>
<result property="categoryName" column="category_name"/>
<result property="code" column="code"/>
<result property="createBy" column="create_by"/>
<result property="createTime" column="create_time"/>
<result property="updateBy" column="update_by"/>
<result property="updateTime" column="update_time"/>
<result property="remark" column="remark"/>
<result property="delFlag" column="del_flag"/>
</resultMap>
</mapper>

View File

@@ -1,23 +0,0 @@
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper
PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.klp.workflow.mapper.WfCopyMapper">
<resultMap type="com.klp.workflow.domain.WfCopy" id="WfCopyResult">
<result property="copyId" column="copy_id"/>
<result property="title" column="title"/>
<result property="processId" column="process_id"/>
<result property="processName" column="process_name"/>
<result property="categoryId" column="category_id"/>
<result property="taskId" column="taskId"/>
<result property="userId" column="user_id"/>
<result property="createBy" column="create_by"/>
<result property="createTime" column="create_time"/>
<result property="updateBy" column="update_by"/>
<result property="updateTime" column="update_time"/>
<result property="delFlag" column="del_flag"/>
</resultMap>
</mapper>

View File

@@ -1,15 +0,0 @@
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper
PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.klp.workflow.mapper.WfDeployFormMapper">
<resultMap type="com.klp.workflow.domain.WfDeployForm" id="WfDeployFormResult">
<result property="deployId" column="deploy_id"/>
<result property="formKey" column="form_key"/>
<result property="nodeKey" column="node_key"/>
<result property="nodeName" column="node_name"/>
<result property="content" column="content"/>
</resultMap>
</mapper>

View File

@@ -1,27 +0,0 @@
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper
PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.klp.workflow.mapper.WfFormMapper">
<resultMap type="com.klp.workflow.domain.WfForm" id="WfFormResult">
<result property="formId" column="form_id"/>
<result property="formName" column="form_name"/>
<result property="content" column="content"/>
<result property="createTime" column="create_time"/>
<result property="updateTime" column="update_time"/>
<result property="createBy" column="create_by"/>
<result property="updateBy" column="update_by"/>
<result property="remark" column="remark"/>
</resultMap>
<select id="selectFormVoList" resultType="com.klp.workflow.domain.vo.WfFormVo">
SELECT
t1.form_id AS formId,
t1.form_name AS formName,
t1.content AS content
FROM wf_form t1
LEFT JOIN wf_deploy_form t2 ON t1.form_id = t2.form_id
${ew.getCustomSqlSegment}
</select>
</mapper>

View File

@@ -321,13 +321,6 @@
<version>${klp-flowable-plus.version}</version>
</dependency>
<!-- 工作流模块 -->
<dependency>
<groupId>com.klp</groupId>
<artifactId>klp-flowable</artifactId>
<version>${klp-flowable-plus.version}</version>
</dependency>
<!-- OSS对象存储模块 -->
<dependency>
<groupId>com.klp</groupId>
@@ -411,7 +404,6 @@
<module>klp-common</module>
<module>klp-demo</module>
<module>klp-extend</module>
<module>klp-flowable</module>
<module>klp-framework</module>
<module>klp-generator</module>
<module>klp-job</module>