From ab67fcc3630baf4f58b6052884de000398e7e97e Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?=E5=A7=9C=E7=8E=89=E7=90=A6?=
<7507756+jiang_yuqi@user.noreply.gitee.com>
Date: Mon, 26 Feb 2024 22:12:58 +0800
Subject: [PATCH] =?UTF-8?q?=E6=8F=90=E4=BA=A4=E4=BB=A3=E7=A0=81?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
---
.../com/yanzhu/common/utils/StringUtils.java | 110 +++++-
.../flowable/common/enums/FlowComment.java | 4 +-
.../controller/FlowDefinitionController.java | 7 +
.../controller/FlowTaskController.java | 13 +
.../com/yanzhu/flowable/flow/ModelUtils.java | 372 ++++++++++++++++++
.../service/impl/FlowTaskServiceImpl.java | 135 ++++---
.../project/domain/ProProjectApply.java | 11 +
.../mapper/project/ProProjectApplyMapper.xml | 4 +-
.../task/myProcess/detail/detailDrawer.vue | 59 +--
.../views/flowable/task/myProcess/index.vue | 106 ++++-
.../task/myProcess/initTaskDrawer.vue | 4 +-
11 files changed, 729 insertions(+), 96 deletions(-)
create mode 100644 yanzhu-flowable/src/main/java/com/yanzhu/flowable/flow/ModelUtils.java
diff --git a/yanzhu-common/src/main/java/com/yanzhu/common/utils/StringUtils.java b/yanzhu-common/src/main/java/com/yanzhu/common/utils/StringUtils.java
index 2086a3b..60f698e 100644
--- a/yanzhu-common/src/main/java/com/yanzhu/common/utils/StringUtils.java
+++ b/yanzhu-common/src/main/java/com/yanzhu/common/utils/StringUtils.java
@@ -1,11 +1,10 @@
package com.yanzhu.common.utils;
-import java.util.ArrayList;
-import java.util.Collection;
-import java.util.HashSet;
-import java.util.List;
-import java.util.Map;
-import java.util.Set;
+import java.nio.ByteBuffer;
+import java.nio.charset.Charset;
+import java.util.*;
+
+import io.netty.util.CharsetUtil;
import org.springframework.util.AntPathMatcher;
import com.yanzhu.common.constant.Constants;
import com.yanzhu.common.core.text.StrFormatter;
@@ -565,6 +564,105 @@ public class StringUtils extends org.apache.commons.lang3.StringUtils
return false;
}
+ /**
+ * 判断指定集合是否包含指定值,如果集合为空(null或者空),返回{@code false},否则找到元素返回{@code true}
+ *
+ * @param collection 集合
+ * @param value 需要查找的值
+ * @return 如果集合为空(null或者空),返回{@code false},否则找到元素返回{@code true}
+ * @throws ClassCastException 如果类型不一致会抛出转换异常
+ * @throws NullPointerException 当指定的元素 值为 null ,或集合类不支持null 时抛出该异常
+ * @see Collection#contains(Object)
+ * @since 4.1.10
+ */
+ public static boolean contains(Collection> collection, Object value) {
+ return isNotEmpty(collection) && collection.contains(value);
+ }
+
+ /**
+ * 将对象转为字符串
+ *
+ *
+ * 1、Byte数组和ByteBuffer会被转换为对应字符串的数组
+ * 2、对象数组会调用Arrays.toString方法
+ *
+ *
+ * @param obj 对象
+ * @return 字符串
+ */
+ public static String utf8Str(Object obj) {
+ return str(obj, CharsetUtil.UTF_8);
+ }
+
+ /**
+ * 将对象转为字符串
+ *
+ * 1、Byte数组和ByteBuffer会被转换为对应字符串的数组
+ * 2、对象数组会调用Arrays.toString方法
+ *
+ *
+ * @param obj 对象
+ * @param charset 字符集
+ * @return 字符串
+ */
+ public static String str(Object obj, Charset charset) {
+ if (null == obj) {
+ return null;
+ }
+
+ if (obj instanceof String) {
+ return (String) obj;
+ } else if (obj instanceof byte[]) {
+ return str((byte[]) obj, charset);
+ } else if (obj instanceof Byte[]) {
+ return str((Byte[]) obj, charset);
+ } else if (obj instanceof ByteBuffer) {
+ return str((ByteBuffer) obj, charset);
+ } else if (isArray(obj)) {
+ return toString(obj);
+ }
+
+ return obj.toString();
+ }
+
+
+ /**
+ * 数组或集合转String
+ *
+ * @param obj 集合或数组对象
+ * @return 数组字符串,与集合转字符串格式相同
+ */
+ public static String toString(Object obj) {
+ if (null == obj) {
+ return null;
+ }
+ if (obj instanceof long[]) {
+ return Arrays.toString((long[]) obj);
+ } else if (obj instanceof int[]) {
+ return Arrays.toString((int[]) obj);
+ } else if (obj instanceof short[]) {
+ return Arrays.toString((short[]) obj);
+ } else if (obj instanceof char[]) {
+ return Arrays.toString((char[]) obj);
+ } else if (obj instanceof byte[]) {
+ return Arrays.toString((byte[]) obj);
+ } else if (obj instanceof boolean[]) {
+ return Arrays.toString((boolean[]) obj);
+ } else if (obj instanceof float[]) {
+ return Arrays.toString((float[]) obj);
+ } else if (obj instanceof double[]) {
+ return Arrays.toString((double[]) obj);
+ } else if (isArray(obj)) {
+ // 对象数组
+ try {
+ return Arrays.deepToString((Object[]) obj);
+ } catch (Exception ignore) {
+ //ignore
+ }
+ }
+ return obj.toString();
+ }
+
/**
* 判断url是否与规则配置:
* ? 表示单个字符;
diff --git a/yanzhu-flowable/src/main/java/com/yanzhu/flowable/common/enums/FlowComment.java b/yanzhu-flowable/src/main/java/com/yanzhu/flowable/common/enums/FlowComment.java
index cd0dbcc..06aa8bf 100644
--- a/yanzhu-flowable/src/main/java/com/yanzhu/flowable/common/enums/FlowComment.java
+++ b/yanzhu-flowable/src/main/java/com/yanzhu/flowable/common/enums/FlowComment.java
@@ -16,8 +16,8 @@ public enum FlowComment {
REJECT("3", "驳回意见"),
DELEGATE("4", "委派意见"),
ASSIGN("5", "转办意见"),
- STOP("6", "终止流程");
-
+ STOP("6", "终止流程"),
+ REVOKE("7", "撤回流程");
/**
* 类型
*/
diff --git a/yanzhu-flowable/src/main/java/com/yanzhu/flowable/controller/FlowDefinitionController.java b/yanzhu-flowable/src/main/java/com/yanzhu/flowable/controller/FlowDefinitionController.java
index 2c974b0..70b11c8 100644
--- a/yanzhu-flowable/src/main/java/com/yanzhu/flowable/controller/FlowDefinitionController.java
+++ b/yanzhu-flowable/src/main/java/com/yanzhu/flowable/controller/FlowDefinitionController.java
@@ -1,10 +1,12 @@
package com.yanzhu.flowable.controller;
+import com.yanzhu.common.annotation.Log;
import com.yanzhu.common.core.controller.BaseController;
import com.yanzhu.common.core.domain.AjaxResult;
import com.yanzhu.common.core.domain.entity.SysRole;
import com.yanzhu.common.core.domain.entity.SysUser;
import com.yanzhu.common.core.text.Convert;
+import com.yanzhu.common.enums.BusinessType;
import com.yanzhu.flowable.domain.FlowSaveXmlVo;
import com.yanzhu.project.domain.ProProjectApply;
import com.yanzhu.system.domain.flowable.FlowQueryVo;
@@ -75,6 +77,7 @@ public class FlowDefinitionController extends BaseController {
}
@ApiOperation(value = "导入流程文件", notes = "上传bpmn20的xml文件")
+ @Log(title = "导入流程文件", businessType = BusinessType.INSERT)
@PostMapping("/import")
public AjaxResult importFile(@RequestParam(required = false) String name,
@RequestParam(required = false) String category,
@@ -136,6 +139,7 @@ public class FlowDefinitionController extends BaseController {
}
@ApiOperation(value = "保存流程设计器内的xml文件")
+ @Log(title = "保存流程设计", businessType = BusinessType.INSERT)
@PostMapping("/save")
public AjaxResult save(@RequestBody FlowSaveXmlVo vo) {
InputStream in = null;
@@ -159,6 +163,7 @@ public class FlowDefinitionController extends BaseController {
@ApiOperation(value = "发起流程")
+ @Log(title = "发起流程申请", businessType = BusinessType.INSERT)
@PostMapping("/start/{procDefId}")
public AjaxResult start(@ApiParam(value = "流程定义id") @PathVariable(value = "procDefId") String procDefId,
@ApiParam(value = "申请表单") @RequestBody ProProjectApply proProjectApply) {
@@ -166,6 +171,7 @@ public class FlowDefinitionController extends BaseController {
}
@ApiOperation(value = "激活或挂起流程定义")
+ @Log(title = "激活/挂起流程", businessType = BusinessType.UPDATE)
@PutMapping(value = "/updateState")
public AjaxResult updateState(@ApiParam(value = "1:激活,2:挂起", required = true) @RequestParam Integer state,
@ApiParam(value = "流程部署ID", required = true) @RequestParam String deployId) {
@@ -174,6 +180,7 @@ public class FlowDefinitionController extends BaseController {
}
@ApiOperation(value = "删除流程")
+ @Log(title = "删除流程定义", businessType = BusinessType.UPDATE)
@DeleteMapping(value = "/{deployIds}")
public AjaxResult delete(@PathVariable String[] deployIds) {
for (String deployId : deployIds) {
diff --git a/yanzhu-flowable/src/main/java/com/yanzhu/flowable/controller/FlowTaskController.java b/yanzhu-flowable/src/main/java/com/yanzhu/flowable/controller/FlowTaskController.java
index 86f7cc5..8c68a8d 100644
--- a/yanzhu-flowable/src/main/java/com/yanzhu/flowable/controller/FlowTaskController.java
+++ b/yanzhu-flowable/src/main/java/com/yanzhu/flowable/controller/FlowTaskController.java
@@ -1,6 +1,8 @@
package com.yanzhu.flowable.controller;
+import com.yanzhu.common.annotation.Log;
import com.yanzhu.common.core.domain.AjaxResult;
+import com.yanzhu.common.enums.BusinessType;
import com.yanzhu.flowable.domain.FlowTaskDto;
import com.yanzhu.system.domain.flowable.FlowQueryVo;
import com.yanzhu.system.domain.flowable.FlowTaskVo;
@@ -41,12 +43,14 @@ public class FlowTaskController {
}
@ApiOperation(value = "取消申请", response = FlowTaskDto.class)
+ @Log(title = "终止申请", businessType = BusinessType.UPDATE)
@PostMapping(value = "/stopProcess")
public AjaxResult stopProcess(@RequestBody FlowTaskVo flowTaskVo) {
return flowTaskService.stopProcess(flowTaskVo);
}
@ApiOperation(value = "撤回流程", response = FlowTaskDto.class)
+ @Log(title = "撤回流程", businessType = BusinessType.UPDATE)
@PostMapping(value = "/revokeProcess")
public AjaxResult revokeProcess(@RequestBody FlowTaskVo flowTaskVo) {
return flowTaskService.revokeProcess(flowTaskVo);
@@ -85,12 +89,14 @@ public class FlowTaskController {
}
@ApiOperation(value = "审批任务")
+ @Log(title = "审批流程", businessType = BusinessType.UPDATE)
@PostMapping(value = "/complete")
public AjaxResult complete(@RequestBody FlowTaskVo flowTaskVo) {
return flowTaskService.complete(flowTaskVo);
}
@ApiOperation(value = "驳回任务")
+ @Log(title = "驳回流程", businessType = BusinessType.UPDATE)
@PostMapping(value = "/reject")
public AjaxResult taskReject(@RequestBody FlowTaskVo flowTaskVo) {
flowTaskService.taskReject(flowTaskVo);
@@ -98,6 +104,7 @@ public class FlowTaskController {
}
@ApiOperation(value = "退回任务")
+ @Log(title = "退回流程", businessType = BusinessType.UPDATE)
@PostMapping(value = "/return")
public AjaxResult taskReturn(@RequestBody FlowTaskVo flowTaskVo) {
flowTaskService.taskReturn(flowTaskVo);
@@ -111,6 +118,7 @@ public class FlowTaskController {
}
@ApiOperation(value = "删除任务")
+ @Log(title = "删除流程", businessType = BusinessType.DELETE)
@DeleteMapping(value = "/delete")
public AjaxResult delete(@RequestBody FlowTaskVo flowTaskVo) {
flowTaskService.deleteTask(flowTaskVo);
@@ -118,6 +126,7 @@ public class FlowTaskController {
}
@ApiOperation(value = "认领/签收任务")
+ @Log(title = "认领/签收流程", businessType = BusinessType.INSERT)
@PostMapping(value = "/claim")
public AjaxResult claim(@RequestBody FlowTaskVo flowTaskVo) {
flowTaskService.claim(flowTaskVo);
@@ -125,6 +134,7 @@ public class FlowTaskController {
}
@ApiOperation(value = "取消认领/签收任务")
+ @Log(title = "取消认领/签收流程", businessType = BusinessType.UPDATE)
@PostMapping(value = "/unClaim")
public AjaxResult unClaim(@RequestBody FlowTaskVo flowTaskVo) {
flowTaskService.unClaim(flowTaskVo);
@@ -132,6 +142,7 @@ public class FlowTaskController {
}
@ApiOperation(value = "委派任务")
+ @Log(title = "委派流程", businessType = BusinessType.UPDATE)
@PostMapping(value = "/delegateTask")
public AjaxResult delegate(@RequestBody FlowTaskVo flowTaskVo) {
flowTaskService.delegateTask(flowTaskVo);
@@ -139,6 +150,7 @@ public class FlowTaskController {
}
@ApiOperation(value = "任务归还")
+ @Log(title = "归还流程", businessType = BusinessType.UPDATE)
@PostMapping(value = "/resolveTask")
public AjaxResult resolveTask(@RequestBody FlowTaskVo flowTaskVo) {
flowTaskService.resolveTask(flowTaskVo);
@@ -146,6 +158,7 @@ public class FlowTaskController {
}
@ApiOperation(value = "转办任务")
+ @Log(title = "转办流程", businessType = BusinessType.UPDATE)
@PostMapping(value = "/assignTask")
public AjaxResult assign(@RequestBody FlowTaskVo flowTaskVo) {
flowTaskService.assignTask(flowTaskVo);
diff --git a/yanzhu-flowable/src/main/java/com/yanzhu/flowable/flow/ModelUtils.java b/yanzhu-flowable/src/main/java/com/yanzhu/flowable/flow/ModelUtils.java
new file mode 100644
index 0000000..b302b44
--- /dev/null
+++ b/yanzhu-flowable/src/main/java/com/yanzhu/flowable/flow/ModelUtils.java
@@ -0,0 +1,372 @@
+package com.yanzhu.flowable.flow;
+
+import com.yanzhu.common.utils.StringUtils;
+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 StringUtils.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 getElementIncomingFlows(FlowElement source) {
+ List sequenceFlows = new ArrayList<>();
+ if (source instanceof FlowNode) {
+ sequenceFlows = ((FlowNode) source).getIncomingFlows();
+ }
+ return sequenceFlows;
+ }
+
+
+ /**
+ * 根据节点,获取出口连线
+ *
+ * @param source 起始节点
+ * @return 出口连线列表
+ */
+ public static List getElementOutgoingFlows(FlowElement source) {
+ List 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 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 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 attributes = baseElement.getAttributes().get(name);
+ if (attributes != null && !attributes.isEmpty()) {
+ attributes.iterator().next().getValue();
+ Iterator 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 (!Objects.isNull(userTask)) {
+ return userTask.hasMultiInstanceLoopCharacteristics();
+ }
+ return false;
+ }
+
+ /**
+ * 获取所有用户任务节点
+ *
+ * @param model bpmnModel对象
+ * @return 用户任务节点列表
+ */
+ public static Collection getAllUserTaskEvent(BpmnModel model) {
+ Process process = model.getMainProcess();
+ Collection flowElements = process.getFlowElements();
+ return getAllUserTaskEvent(flowElements, null);
+ }
+
+ /**
+ * 获取所有用户任务节点
+ * @param flowElements 流程元素集合
+ * @param allElements 所有流程元素集合
+ * @return 用户任务节点列表
+ */
+ public static Collection getAllUserTaskEvent(Collection flowElements, Collection 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 findNextUserTasks(FlowElement source) {
+ return findNextUserTasks(source, null, null);
+ }
+
+ /**
+ * 查找起始节点下一个用户任务列表列表
+ * @param source 起始节点
+ * @param hasSequenceFlow 已经经过的连线的 ID,用于判断线路是否重复
+ * @param userTaskList 用户任务列表
+ * @return 结果
+ */
+ public static List findNextUserTasks(FlowElement source, Set hasSequenceFlow, List userTaskList) {
+ hasSequenceFlow = Optional.ofNullable(hasSequenceFlow).orElse(new HashSet<>());
+ userTaskList = Optional.ofNullable(userTaskList).orElse(new ArrayList<>());
+ // 获取出口连线
+ List 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 visitedElements) {
+ visitedElements = visitedElements == null ? new HashSet<>() : visitedElements;
+ if (source instanceof StartEvent && isInEventSubprocess(source)) {
+ return false;
+ }
+
+ // 根据类型,获取入口连线
+ List 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;
+ }
+}
diff --git a/yanzhu-flowable/src/main/java/com/yanzhu/flowable/service/impl/FlowTaskServiceImpl.java b/yanzhu-flowable/src/main/java/com/yanzhu/flowable/service/impl/FlowTaskServiceImpl.java
index 95ce006..9b94349 100644
--- a/yanzhu-flowable/src/main/java/com/yanzhu/flowable/service/impl/FlowTaskServiceImpl.java
+++ b/yanzhu-flowable/src/main/java/com/yanzhu/flowable/service/impl/FlowTaskServiceImpl.java
@@ -5,6 +5,8 @@ import com.alibaba.fastjson2.JSON;
import com.alibaba.fastjson2.JSONObject;
import com.alibaba.fastjson2.TypeReference;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
+import com.yanzhu.common.core.text.Convert;
+import com.yanzhu.common.utils.StringUtils;
import com.yanzhu.flowable.common.constant.ProcessConstants;
import com.yanzhu.common.core.domain.AjaxResult;
import com.yanzhu.common.core.domain.entity.SysRole;
@@ -16,6 +18,7 @@ import com.yanzhu.flowable.domain.FlowCommentDto;
import com.yanzhu.flowable.domain.FlowNextDto;
import com.yanzhu.flowable.domain.FlowTaskDto;
import com.yanzhu.flowable.domain.FlowViewerDto;
+import com.yanzhu.flowable.flow.ModelUtils;
import com.yanzhu.system.domain.flowable.FlowQueryVo;
import com.yanzhu.system.domain.flowable.FlowTaskVo;
import com.yanzhu.flowable.factory.FlowServiceFactory;
@@ -31,11 +34,11 @@ import com.yanzhu.system.service.ISysUserService;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.collections4.CollectionUtils;
import org.apache.commons.io.IOUtils;
-import org.apache.commons.lang3.StringUtils;
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;
@@ -590,6 +593,7 @@ public class FlowTaskServiceImpl extends FlowServiceFactory implements IFlowTask
* @return
*/
@Override
+ @Transactional
public AjaxResult stopProcess(FlowTaskVo flowTaskVo) {
List task = taskService.createTaskQuery().processInstanceId(flowTaskVo.getInstanceId()).list();
if (CollectionUtils.isEmpty(task)) {
@@ -604,25 +608,27 @@ public class FlowTaskServiceImpl extends FlowServiceFactory implements IFlowTask
Process process = bpmnModel.getMainProcess();
List endNodes = process.findFlowElementsOfType(EndEvent.class, false);
if (CollectionUtils.isNotEmpty(endNodes)) {
- // TODO 取消流程为什么要设置流程发起人?
-// SysUser loginUser = SecurityUtils.getLoginUser().getUser();
-// Authentication.setAuthenticatedUserId(loginUser.getUserId().toString());
-
-// taskService.addComment(task.getId(), processInstance.getProcessInstanceId(), FlowComment.STOP.getType(),
-// StringUtils.isBlank(flowTaskVo.getComment()) ? "取消申请" : flowTaskVo.getComment());
- // 获取当前流程最后一个节点
- String endId = endNodes.get(0).getId();
- List executions = runtimeService.createExecutionQuery()
- .parentId(processInstance.getProcessInstanceId()).list();
- List executionIds = new ArrayList<>();
- executions.forEach(execution -> executionIds.add(execution.getId()));
- // 变更流程为已结束状态
- runtimeService.createChangeActivityStateBuilder()
- .moveExecutionsToSingleActivityId(executionIds, endId).changeState();
+ String userIdStr = Convert.toStr(SecurityUtils.getUserId());
+ if(StringUtils.isNotEmpty(userIdStr)){
+ Authentication.setAuthenticatedUserId(userIdStr);
+ taskService.addComment(task.get(0).getId(), processInstance.getProcessInstanceId(), FlowComment.STOP.getType(),
+ StringUtils.isBlank(flowTaskVo.getComment()) ? "取消申请" : flowTaskVo.getComment());
+ // 设置流程经办人为当前登录人员
+ taskService.setAssignee(task.get(0).getId(), userIdStr);
+ // 获取当前流程最后一个节点
+ String endId = endNodes.get(0).getId();
+ List executions = runtimeService.createExecutionQuery()
+ .parentId(processInstance.getProcessInstanceId()).list();
+ List executionIds = new ArrayList<>();
+ executions.forEach(execution -> executionIds.add(execution.getId()));
+ // 变更流程为已结束状态
+ runtimeService.createChangeActivityStateBuilder()
+ .moveExecutionsToSingleActivityId(executionIds, endId).changeState();
+ return AjaxResult.success();
+ }
}
}
-
- return AjaxResult.success();
+ return AjaxResult.error();
}
/**
@@ -632,55 +638,66 @@ public class FlowTaskServiceImpl extends FlowServiceFactory implements IFlowTask
* @return
*/
@Override
+ @Transactional
public AjaxResult revokeProcess(FlowTaskVo flowTaskVo) {
- Task task = taskService.createTaskQuery().processInstanceId(flowTaskVo.getInstanceId()).singleResult();
- if (task == null) {
- throw new CustomException("流程未启动或已执行完成,无法撤回");
+ String procInsId = flowTaskVo.getInstanceId();
+ String taskId = flowTaskVo.getTaskId();
+ // 校验流程是否结束
+ ProcessInstance processInstance = runtimeService.createProcessInstanceQuery()
+ .processInstanceId(procInsId)
+ .active()
+ .singleResult();
+ if(Objects.isNull(processInstance)) {
+ throw new RuntimeException("流程已结束或已挂起,无法执行撤回操作");
}
+ // 获取待撤回任务实例
+ HistoricTaskInstance currTaskIns = historyService.createHistoricTaskInstanceQuery()
+ .taskId(taskId)
+ .taskAssignee(flowTaskVo.getUserId())
+ .singleResult();
+ if (Objects.isNull(currTaskIns)) {
+ throw new RuntimeException("当前任务不存在,无法执行撤回操作");
+ }
+ // 获取 bpmn 模型
+ BpmnModel bpmnModel = repositoryService.getBpmnModel(currTaskIns.getProcessDefinitionId());
+ UserTask currUserTask = ModelUtils.getUserTaskByKey(bpmnModel, currTaskIns.getTaskDefinitionKey());
+ // 查找下一级用户任务列表
+ List nextUserTaskList = ModelUtils.findNextUserTasks(currUserTask);
+ List nextUserTaskKeys = nextUserTaskList.stream().map(UserTask::getId).collect(Collectors.toList());
- SysUser loginUser = SecurityUtils.getLoginUser().getUser();
- List htiList = historyService.createHistoricTaskInstanceQuery()
- .processInstanceId(task.getProcessInstanceId())
- .orderByTaskCreateTime()
- .asc()
+ // 获取当前节点之后已完成的流程历史节点
+ List finishedTaskInsList = historyService.createHistoricTaskInstanceQuery()
+ .processInstanceId(procInsId)
+ .taskCreatedAfter(currTaskIns.getEndTime())
+ .finished()
.list();
- String myTaskId = null;
- HistoricTaskInstance myTask = null;
- for (HistoricTaskInstance hti : htiList) {
- if (loginUser.getUserId().toString().equals(hti.getAssignee())) {
- myTaskId = hti.getId();
- myTask = hti;
- break;
+ for (HistoricTaskInstance finishedTaskInstance : finishedTaskInsList) {
+ // 检查已完成流程历史节点是否存在下一级中
+ if (StringUtils.contains(nextUserTaskKeys, finishedTaskInstance.getTaskDefinitionKey())) {
+ throw new RuntimeException("下一流程已处理,无法执行撤回操作");
}
}
- if (null == myTaskId) {
- throw new CustomException("该任务非当前用户提交,无法撤回");
- }
-
- String processDefinitionId = myTask.getProcessDefinitionId();
- BpmnModel bpmnModel = repositoryService.getBpmnModel(processDefinitionId);
-
- //变量
-// Map variables = runtimeService.getVariableInstances(currentTask.getExecutionId());
- String myActivityId = null;
- List haiList = historyService.createHistoricActivityInstanceQuery()
- .executionId(myTask.getExecutionId()).finished().list();
- for (HistoricActivityInstance hai : haiList) {
- if (myTaskId.equals(hai.getTaskId())) {
- myActivityId = hai.getActivityId();
- break;
+ // 获取所有激活的任务节点,找到需要撤回的任务
+ List activateTaskList = taskService.createTaskQuery().processInstanceId(procInsId).list();
+ List revokeExecutionIds = new ArrayList<>();
+ for (Task task : activateTaskList) {
+ // 检查激活的任务节点是否存在下一级中,如果存在,则加入到需要撤回的节点
+ if (StringUtils.contains(nextUserTaskKeys, task.getTaskDefinitionKey())) {
+ // 添加撤回审批信息
+ taskService.setAssignee(task.getId(), flowTaskVo.getUserId());
+ taskService.addComment(task.getId(), task.getProcessInstanceId(), FlowComment.REVOKE.getType(), flowTaskVo.getAssignee() + "撤回流程审批");
+ revokeExecutionIds.add(task.getExecutionId());
}
}
- FlowNode myFlowNode = (FlowNode) bpmnModel.getMainProcess().getFlowElement(myActivityId);
-
- Execution execution = runtimeService.createExecutionQuery().executionId(task.getExecutionId()).singleResult();
- String activityId = execution.getActivityId();
- FlowNode flowNode = (FlowNode) bpmnModel.getMainProcess().getFlowElement(activityId);
-
- //记录原活动方向
- List oriSequenceFlows = new ArrayList<>(flowNode.getOutgoingFlows());
-
-
+ try {
+ runtimeService.createChangeActivityStateBuilder()
+ .processInstanceId(procInsId)
+ .moveExecutionsToSingleActivityId(revokeExecutionIds, currTaskIns.getTaskDefinitionKey()).changeState();
+ } catch (FlowableObjectNotFoundException e) {
+ throw new RuntimeException("未找到流程实例,流程可能已发生变化");
+ } catch (FlowableException e) {
+ throw new RuntimeException("执行撤回操作失败");
+ }
return AjaxResult.success();
}
diff --git a/yanzhu-mapper/src/main/java/com/yanzhu/project/domain/ProProjectApply.java b/yanzhu-mapper/src/main/java/com/yanzhu/project/domain/ProProjectApply.java
index 81f3cab..364ec60 100644
--- a/yanzhu-mapper/src/main/java/com/yanzhu/project/domain/ProProjectApply.java
+++ b/yanzhu-mapper/src/main/java/com/yanzhu/project/domain/ProProjectApply.java
@@ -66,6 +66,9 @@ public class ProProjectApply extends BaseEntity
@Excel(name = "是否删除")
private String isDel;
+ /** 申请人单位名称 */
+ private String createByDeptName;
+
/** 项目申请明细信息 */
private List proProjectApplyDetailList;
@@ -176,6 +179,14 @@ public class ProProjectApply extends BaseEntity
this.parProjName = parProjName;
}
+ public String getCreateByDeptName() {
+ return createByDeptName;
+ }
+
+ public void setCreateByDeptName(String createByDeptName) {
+ this.createByDeptName = createByDeptName;
+ }
+
public List getProProjectApplyDetailList()
{
return proProjectApplyDetailList;
diff --git a/yanzhu-mapper/src/main/resources/mapper/project/ProProjectApplyMapper.xml b/yanzhu-mapper/src/main/resources/mapper/project/ProProjectApplyMapper.xml
index 6879172..bffede7 100644
--- a/yanzhu-mapper/src/main/resources/mapper/project/ProProjectApplyMapper.xml
+++ b/yanzhu-mapper/src/main/resources/mapper/project/ProProjectApplyMapper.xml
@@ -17,6 +17,7 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
+
@@ -71,9 +72,10 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
diff --git a/yanzhu-ui/src/views/flowable/task/myProcess/detail/detailDrawer.vue b/yanzhu-ui/src/views/flowable/task/myProcess/detail/detailDrawer.vue
index 2b120ce..531ac4f 100644
--- a/yanzhu-ui/src/views/flowable/task/myProcess/detail/detailDrawer.vue
+++ b/yanzhu-ui/src/views/flowable/task/myProcess/detail/detailDrawer.vue
@@ -98,10 +98,10 @@
- {{ initData.projParName }}
+ {{ initData.parProjName }}
- {{ initData.projParName }}
+ {{ initData.projName }}
{{ title }}
@@ -131,16 +131,23 @@
{{ parseTime(initData.createTime, "{y}-{m}-{d} {h}:{i}") }}
-
- {{ initData.applyReason }}
+
+ {{ parseTime(initData.useTime, "{y}-{m}-{d} {h}:{i}") }}
- 申请明细
-
-
+ 申请明细信息
+
+
- {{ scope.row.superTypeName }}
{{ scope.row.typeName }}
{{
scope.row.assetsName
@@ -148,24 +155,25 @@
-
-
+
+
-
+
-
+
关 闭
@@ -204,6 +212,7 @@ export default {
//label样式
labelStyle: { width: "180px" },
initData: {},
+ showAssetsVersion: false,
};
},
computed: {},
@@ -246,6 +255,12 @@ export default {
},
show(options) {
this.options = options;
+ //物资设备类需要输入规格
+ if (options.category == 1) {
+ this.showAssetsVersion = true;
+ } else {
+ this.showAssetsVersion = false;
+ }
this.title = options.procDefName;
this.deptName = options.startDeptName;
this.nickName = options.startUserName;
@@ -571,8 +586,8 @@ export default {
}
}
}
-.assetsName {
- font-weight: 800;
- color: #409eff;
+.assetsName .el-breadcrumb__inner {
+ font-weight: 800 !important;
+ color: #409eff !important;
}
diff --git a/yanzhu-ui/src/views/flowable/task/myProcess/index.vue b/yanzhu-ui/src/views/flowable/task/myProcess/index.vue
index 2a606b1..0cf8229 100644
--- a/yanzhu-ui/src/views/flowable/task/myProcess/index.vue
+++ b/yanzhu-ui/src/views/flowable/task/myProcess/index.vue
@@ -189,22 +189,41 @@
- 处理
+ 详情
- 取消申请
删除
@@ -319,6 +338,36 @@
@pagination="listDefinition"
/>
+
+
+
+
+
+
+
+
+
@@ -349,6 +398,7 @@ export default {
return {
// 遮罩层
loading: true,
+ stopLoading: false,
processLoading: true,
// 选中数组
ids: [],
@@ -404,7 +454,16 @@ export default {
// 表单参数
form: {},
// 表单校验
- rules: {},
+ rules: {
+ comment: [
+ { required: true, message: "请输入取消原因", trigger: "blur" },
+ {
+ max: 500,
+ message: "取消原因最多输入500字",
+ trigger: "blur",
+ },
+ ],
+ },
tabs: {
all: 0,
await: 0,
@@ -496,6 +555,18 @@ export default {
this.single = selection.length !== 1;
this.multiple = !selection.length;
},
+ /** 继续办理 */
+ handleActivate(row) {
+ this.$refs.editTaskDrawer.show(row);
+ },
+ /** 判断是否继续办理 */
+ getActivate(row) {
+ if (row.taskName == "提交申请" && row.assigneeId == this.$store.getters.userId) {
+ return true;
+ } else {
+ return false;
+ }
+ },
/** 新增按钮操作 */
handleAdd() {
this.open = true;
@@ -524,6 +595,33 @@ export default {
this.getList();
});
},
+ /** 取消流程申请 */
+ handleStop(row) {
+ this.form.taskId = row.taskId;
+ this.form.userId = this.$store.getters.userId;
+ this.form.instanceId = row.procInsId;
+ this.stopOpen = true;
+ },
+ /** 取消流程申请 */
+ handleStopClick() {
+ let that = this;
+ this.$refs["form"].validate((valid) => {
+ if (valid) {
+ this.$modal
+ .confirm("是否确认取消当前流程申请?")
+ .then(function () {
+ that.stopLoading = true;
+ return stopProcess(that.form);
+ })
+ .then((res) => {
+ that.$modal.msgSuccess(res.msg);
+ that.stopLoading = false;
+ that.stopOpen = false;
+ that.getList();
+ });
+ }
+ });
+ },
/** 流程流转记录 */
handleFlowRecord(row) {
/** this.$router.push({
diff --git a/yanzhu-ui/src/views/flowable/task/myProcess/initTaskDrawer.vue b/yanzhu-ui/src/views/flowable/task/myProcess/initTaskDrawer.vue
index 7273bce..fe0c701 100644
--- a/yanzhu-ui/src/views/flowable/task/myProcess/initTaskDrawer.vue
+++ b/yanzhu-ui/src/views/flowable/task/myProcess/initTaskDrawer.vue
@@ -153,7 +153,7 @@
label="申请规格"
align="center"
prop="assetsVersion"
- width="160"
+ width="180"
>
-