提交代码

main
姜玉琦 2024-02-26 22:12:58 +08:00
parent 1a776f79ea
commit ab67fcc363
11 changed files with 729 additions and 96 deletions

View File

@ -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);
}
/**
* <br>
*
* <pre>
* 1ByteByteBuffer
* 2Arrays.toString
* </pre>
*
* @param obj
* @return
*/
public static String utf8Str(Object obj) {
return str(obj, CharsetUtil.UTF_8);
}
/**
*
* <pre>
* 1ByteByteBuffer
* 2Arrays.toString
* </pre>
*
* @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:
* ? ;

View File

@ -16,8 +16,8 @@ public enum FlowComment {
REJECT("3", "驳回意见"),
DELEGATE("4", "委派意见"),
ASSIGN("5", "转办意见"),
STOP("6", "终止流程");
STOP("6", "终止流程"),
REVOKE("7", "撤回流程");
/**
*
*/

View File

@ -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) {

View File

@ -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);

View File

@ -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();
/**
* xmlbpmnModel
*
* @param xml xml
* @return bpmnModel
*/
public static BpmnModel getBpmnModel(String xml) {
return bpmnXMLConverter.convertToBpmnModel(new StringStreamSource(xml), false, false);
}
/**
* bpmnModelxml
*
* @deprecated bpmn 线
* @param bpmnModel bpmnModel
* @return xml
*/
@Deprecated
public static String getBpmnXmlStr(BpmnModel bpmnModel) {
return StringUtils.utf8Str(getBpmnXml(bpmnModel));
}
/**
* bpmnModelxml
*
* @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 (!Objects.isNull(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

@ -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> 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<EndEvent> 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<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();
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<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();
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<UserTask> nextUserTaskList = ModelUtils.findNextUserTasks(currUserTask);
List<String> nextUserTaskKeys = nextUserTaskList.stream().map(UserTask::getId).collect(Collectors.toList());
SysUser loginUser = SecurityUtils.getLoginUser().getUser();
List<HistoricTaskInstance> htiList = historyService.createHistoricTaskInstanceQuery()
.processInstanceId(task.getProcessInstanceId())
.orderByTaskCreateTime()
.asc()
// 获取当前节点之后已完成的流程历史节点
List<HistoricTaskInstance> 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<String, VariableInstance> variables = runtimeService.getVariableInstances(currentTask.getExecutionId());
String myActivityId = null;
List<HistoricActivityInstance> haiList = historyService.createHistoricActivityInstanceQuery()
.executionId(myTask.getExecutionId()).finished().list();
for (HistoricActivityInstance hai : haiList) {
if (myTaskId.equals(hai.getTaskId())) {
myActivityId = hai.getActivityId();
break;
// 获取所有激活的任务节点,找到需要撤回的任务
List<Task> activateTaskList = taskService.createTaskQuery().processInstanceId(procInsId).list();
List<String> 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<SequenceFlow> 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();
}

View File

@ -66,6 +66,9 @@ public class ProProjectApply extends BaseEntity
@Excel(name = "是否删除")
private String isDel;
/** 申请人单位名称 */
private String createByDeptName;
/** 项目申请明细信息 */
private List<ProProjectApplyDetail> 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<ProProjectApplyDetail> getProProjectApplyDetailList()
{
return proProjectApplyDetailList;

View File

@ -17,6 +17,7 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
<result property="applyUser" column="apply_user" />
<result property="useTime" column="use_time" />
<result property="isDel" column="is_del" />
<result property="createByDeptName" column="createByDeptName" />
<result property="createBy" column="create_by" />
<result property="createTime" column="create_time" />
<result property="updateBy" column="update_by" />
@ -71,9 +72,10 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
</select>
<select id="selectProProjectApplyById" parameterType="Long" resultMap="ProProjectApplyProProjectApplyDetailResult">
select a.id, a.dept_id, a.proj_id, a.proj_name, a.par_proj_name, a.apply_type, a.apply_status, a.apply_reason, a.apply_files, a.apply_user, a.use_time, a.is_del, a.create_by, a.create_time, a.update_by, a.update_time, a.remark,
select a.id, a.dept_id, a.proj_id, a.proj_name, a.par_proj_name, a.apply_type, a.apply_status, a.apply_reason, a.apply_files, a.apply_user, a.use_time, a.is_del, sd.dept_name as createByDeptName, a.create_by, a.create_time, a.update_by, a.update_time, a.remark,
b.id as sub_id, b.apply_id as sub_apply_id, b.super_type_key as sub_super_type_key, b.super_type_name as sub_super_type_name, b.type_id as sub_type_id, b.type_name as sub_type_name, b.assets_id as sub_assets_id, b.assets_name as sub_assets_name, b.assets_unit as sub_assets_unit, b.number as sub_number, b.use_time as sub_use_time, b.use_reason as sub_use_reason, b.price as sub_price, b.total_price as sub_total_price, b.is_del as sub_is_del, b.create_by as sub_create_by, b.create_time as sub_create_time, b.update_by as sub_update_by, b.update_time as sub_update_time, b.remark as sub_remark
from pro_project_apply a
left join sys_dept sd on a.dept_id = sd.dept_id
left join pro_project_apply_detail b on b.apply_id = a.id
where a.id = #{id}
</select>

View File

@ -98,10 +98,10 @@
<div class="drawerRight">
<el-form label-width="80px">
<el-form-item label="项目单位">
{{ initData.projParName }}
{{ initData.parProjName }}
</el-form-item>
<el-form-item label="项目名称">
{{ initData.projParName }}
{{ initData.projName }}
</el-form-item>
<el-form-item label="申请类型">
{{ title }}
@ -131,16 +131,23 @@
<el-form-item label="申请时间">
{{ parseTime(initData.createTime, "{y}-{m}-{d} {h}:{i}") }}
</el-form-item>
<el-form-item label="使用时间">
{{ initData.applyReason }}
<el-form-item label="使用时间" v-if="initData.useTime">
{{ parseTime(initData.useTime, "{y}-{m}-{d} {h}:{i}") }}
</el-form-item>
<el-divider content-position="left">申请明细</el-divider>
<el-table stripe :data="initData.proProjectApplyDetailList">
<el-table-column label="序号" width="40" type="index"></el-table-column>
<el-divider content-position="left">申请明细信息</el-divider>
<el-table
ref="proProjectApplyDetail"
:data="initData.proProjectApplyDetailList"
>
<el-table-column
label="序号"
width="50"
type="index"
align="center"
></el-table-column>
<el-table-column label="申请明细" align="left">
<template slot-scope="scope">
<el-breadcrumb separator=">">
<el-breadcrumb-item>{{ scope.row.superTypeName }}</el-breadcrumb-item>
<el-breadcrumb-item>{{ scope.row.typeName }}</el-breadcrumb-item>
<el-breadcrumb-item class="assetsName">{{
scope.row.assetsName
@ -148,24 +155,25 @@
</el-breadcrumb>
</template>
</el-table-column>
<el-table-column label="规格" prop="assetsVersion"></el-table-column>
<el-table-column label="数量" prop="number">
<el-table-column
label="申请规格"
prop="assetsVersion"
width="180"
align="center"
v-if="showAssetsVersion"
></el-table-column>
<el-table-column label="申请数量" prop="number" width="180" align="center">
<template slot-scope="scope">
<label
>{{ scope.row.number }}
<el-tag type="info" size="mini">{{
scope.row.assetsUnit
}}</el-tag></label
>
<label>{{ scope.row.number }} {{ scope.row.assetsUnit }}</label>
</template>
</el-table-column>
<el-table-column
label="说明"
label="使用说明"
prop="useReason"
width="150"
align="center"
></el-table-column>
</el-table>
<div style="text-align: center">
<div style="text-align: center; margin: 20px 0px">
<el-button type="danger" @click="doCanel"> </el-button>
</div>
</el-form>
@ -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;
}
</style>

View File

@ -189,22 +189,41 @@
</el-table-column>
<el-table-column
label="操作"
width="150"
width="200"
fixed="right"
class-name="small-padding fixed-width"
>
<template slot-scope="scope">
<el-button @click="handleFlowRecord(scope.row)" type="text" size="small"
<el-button
v-if="getActivate(scope.row)"
@click="handleActivate(scope.row)"
type="text"
size="small"
icon="el-icon-edit-outline"
>处理</el-button
>
<el-button
@click="handleFlowRecord(scope.row)"
type="text"
size="small"
icon="el-icon-finished"
>详情</el-button
>
<el-button @click="handleStop(scope.row)" type="text" size="small"
<el-button
v-if="scope.row.finishTime == null"
@click="handleStop(scope.row)"
type="text"
size="small"
icon="el-icon-switch-button"
>取消申请</el-button
>
<el-button
v-if="scope.row.finishTime == null"
@click="handleDelete(scope.row)"
type="text"
size="small"
v-hasPermi="['system:deployment:remove']"
icon="el-icon-delete"
>删除</el-button
>
</template>
@ -319,6 +338,36 @@
@pagination="listDefinition"
/>
</el-dialog>
<!-- 终止流程 -->
<el-dialog
:title="title"
:visible.sync="stopOpen"
width="30%"
append-to-body
:close-on-click-modal="false"
:close-on-press-escape="false"
>
<el-form
ref="form"
:model="form"
:rules="rules"
v-loading="stopLoading"
label-width="80px"
>
<el-form-item label="取消原因" prop="comment">
<el-input
type="textarea"
v-model="form.comment"
placeholder="请输入取消原因"
rows="5"
/>
</el-form-item>
</el-form>
<div slot="footer" class="dialog-footer">
<el-button type="primary" @click="handleStopClick"> </el-button>
<el-button @click="cancel"> </el-button>
</div>
</el-dialog>
<detailDrawer ref="detailDrawer"></detailDrawer>
<editTaskDrawer ref="editTaskDrawer" :closeCallBack="getList"></editTaskDrawer>
<initTaskDrawer ref="initTaskDrawer" :closeCallBack="getList"></initTaskDrawer>
@ -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({

View File

@ -153,7 +153,7 @@
label="申请规格"
align="center"
prop="assetsVersion"
width="160"
width="180"
>
<template slot-scope="scope">
<el-input
@ -180,7 +180,7 @@
</template>
</el-table-column>
</el-table>
<div style="text-align: center; margin: 20px 0px">
<div style="text-align: center; margin: 20px 0px;">
<el-button type="primary" @click="submitForm"></el-button>
<el-button @click="doCanel"> </el-button>
</div>