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}") }} - 申请明细 - - + 申请明细信息 + + - - + + -
+
关 闭
@@ -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" >