fix(流程审批): 移除流程驳回,新增审批拒绝,拒绝操作会直接终止流程。

This commit is contained in:
konbai
2022-10-09 22:59:32 +08:00
parent 4ab600987b
commit 4d90e375bb
6 changed files with 71 additions and 124 deletions

View File

@@ -72,12 +72,12 @@ public class WfTaskController {
}
/**
* 驳回任务
* 拒绝任务
*/
@PostMapping(value = "/reject")
@SaCheckPermission("workflow:process:approval")
public R taskReject(@RequestBody WfTaskBo bo) {
flowTaskService.taskReject(bo);
public R taskReject(@RequestBody WfTaskBo taskBo) {
flowTaskService.taskReject(taskBo);
return R.ok();
}

View File

@@ -66,6 +66,32 @@ public class ModelUtils {
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;
}
/**
* 获取所有用户任务节点
*

View File

@@ -24,11 +24,11 @@ public interface IWfTaskService {
void complete(WfTaskBo task);
/**
* 驳回任务
* 拒绝任务
*
* @param bo
* @param taskBo
*/
void taskReject(WfTaskBo bo);
void taskReject(WfTaskBo taskBo);
/**

View File

@@ -16,6 +16,7 @@ import com.ruoyi.flowable.factory.FlowServiceFactory;
import com.ruoyi.flowable.flow.CustomProcessDiagramGenerator;
import com.ruoyi.flowable.flow.FindNextNodeUtil;
import com.ruoyi.flowable.flow.FlowableUtils;
import com.ruoyi.flowable.utils.ModelUtils;
import com.ruoyi.flowable.utils.TaskUtils;
import com.ruoyi.system.service.ISysRoleService;
import com.ruoyi.system.service.ISysUserService;
@@ -103,132 +104,48 @@ public class WfTaskServiceImpl extends FlowServiceFactory implements IWfTaskServ
}
/**
* 驳回任务
* 拒绝任务
*
* @param bo
* @param taskBo
*/
@Override
public void taskReject(WfTaskBo bo) {
@Transactional(rollbackFor = Exception.class)
public void taskReject(WfTaskBo taskBo) {
// 当前任务 task
Task task = taskService.createTaskQuery().taskId(bo.getTaskId()).singleResult();
Task task = taskService.createTaskQuery().taskId(taskBo.getTaskId()).singleResult();
if (ObjectUtil.isNull(task)) {
throw new RuntimeException("获取任务信息异常!");
}
if (task.isSuspended()) {
throw new RuntimeException("任务处于挂起状态");
}
// 获取流程实例
ProcessInstance processInstance = runtimeService.createProcessInstanceQuery()
.processInstanceId(taskBo.getProcInsId())
.singleResult();
if (processInstance == null) {
throw new RuntimeException("流程实例不存在,请确认!");
}
// 获取流程定义信息
ProcessDefinition processDefinition = repositoryService.createProcessDefinitionQuery().processDefinitionId(task.getProcessDefinitionId()).singleResult();
ProcessDefinition processDefinition = repositoryService.createProcessDefinitionQuery()
.processDefinitionId(task.getProcessDefinitionId())
.singleResult();
// 添加审批意见
taskService.addComment(taskBo.getTaskId(), taskBo.getProcInsId(), FlowComment.REJECT.getType(), taskBo.getComment());
// 获取所有节点信息
Process process = repositoryService.getBpmnModel(processDefinition.getId()).getProcesses().get(0);
// 获取全部节点列表,包含子节点
Collection<FlowElement> allElements = FlowableUtils.getAllElements(process.getFlowElements(), null);
// 获取当前任务节点元素
FlowElement source = null;
if (allElements != null) {
for (FlowElement flowElement : allElements) {
// 类型为用户节点
if (flowElement.getId().equals(task.getTaskDefinitionKey())) {
// 获取节点信息
source = flowElement;
}
}
}
// 目的获取所有跳转到的节点 targetIds
// 获取当前节点的所有父级用户任务节点
// 深度优先算法思想:延边迭代深入
List<UserTask> parentUserTaskList = FlowableUtils.iteratorFindParentUserTasks(source, null, null);
if (parentUserTaskList == null || parentUserTaskList.size() == 0) {
throw new RuntimeException("当前节点为初始任务节点,不能驳回");
}
// 获取活动 ID 即节点 Key
List<String> parentUserTaskKeyList = new ArrayList<>();
parentUserTaskList.forEach(item -> parentUserTaskKeyList.add(item.getId()));
// 获取全部历史节点活动实例,即已经走过的节点历史,数据采用开始时间升序
List<HistoricTaskInstance> historicTaskInstanceList = historyService.createHistoricTaskInstanceQuery().processInstanceId(task.getProcessInstanceId()).orderByHistoricTaskInstanceStartTime().asc().list();
// 数据清洗,将回滚导致的脏数据清洗掉
List<String> lastHistoricTaskInstanceList = FlowableUtils.historicTaskInstanceClean(allElements, historicTaskInstanceList);
// 此时历史任务实例为倒序,获取最后走的节点
List<String> targetIds = new ArrayList<>();
// 循环结束标识,遇到当前目标节点的次数
int number = 0;
StringBuilder parentHistoricTaskKey = new StringBuilder();
for (String historicTaskInstanceKey : lastHistoricTaskInstanceList) {
// 当会签时候会出现特殊的,连续都是同一个节点历史数据的情况,这种时候跳过
if (parentHistoricTaskKey.toString().equals(historicTaskInstanceKey)) {
continue;
}
parentHistoricTaskKey = new StringBuilder(historicTaskInstanceKey);
if (historicTaskInstanceKey.equals(task.getTaskDefinitionKey())) {
number++;
}
// 在数据清洗后,历史节点就是唯一一条从起始到当前节点的历史记录,理论上每个点只会出现一次
// 在流程中如果出现循环,那么每次循环中间的点也只会出现一次,再出现就是下次循环
// number == 1第一次遇到当前节点
// number == 2第二次遇到代表最后一次的循环范围
if (number == 2) {
break;
}
// 如果当前历史节点,属于父级的节点,说明最后一次经过了这个点,需要退回这个点
if (parentUserTaskKeyList.contains(historicTaskInstanceKey)) {
targetIds.add(historicTaskInstanceKey);
}
}
// 目的获取所有需要被跳转的节点 currentIds
// 取其中一个父级任务,因为后续要么存在公共网关,要么就是串行公共线路
UserTask oneUserTask = parentUserTaskList.get(0);
// 获取所有正常进行的任务节点 Key这些任务不能直接使用需要找出其中需要撤回的任务
List<Task> runTaskList = taskService.createTaskQuery().processInstanceId(task.getProcessInstanceId()).list();
List<String> runTaskKeyList = new ArrayList<>();
runTaskList.forEach(item -> runTaskKeyList.add(item.getTaskDefinitionKey()));
// 需驳回任务列表
List<String> currentIds = new ArrayList<>();
// 通过父级网关的出口连线,结合 runTaskList 比对,获取需要撤回的任务
List<UserTask> currentUserTaskList = FlowableUtils.iteratorFindChildUserTasks(oneUserTask, runTaskKeyList, null, null);
currentUserTaskList.forEach(item -> currentIds.add(item.getId()));
// 规定:并行网关之前节点必须需存在唯一用户任务节点,如果出现多个任务节点,则并行网关节点默认为结束节点,原因为不考虑多对多情况
if (targetIds.size() > 1 && currentIds.size() > 1) {
throw new RuntimeException("任务出现多对多情况,无法撤回");
}
// 循环获取那些需要被撤回的节点的ID用来设置驳回原因
List<String> currentTaskIds = new ArrayList<>();
currentIds.forEach(currentId -> runTaskList.forEach(runTask -> {
if (currentId.equals(runTask.getTaskDefinitionKey())) {
currentTaskIds.add(runTask.getId());
}
}));
// 设置驳回意见
currentTaskIds.forEach(item -> taskService.addComment(item, task.getProcessInstanceId(), FlowComment.REJECT.getType(), bo.getComment()));
try {
// 如果父级任务多于 1 个,说明当前节点不是并行节点,原因为不考虑多对多情况
if (targetIds.size() > 1) {
// 1 对 多任务跳转currentIds 当前节点(1)targetIds 跳转到的节点(多)
runtimeService.createChangeActivityStateBuilder()
.processInstanceId(task.getProcessInstanceId()).
moveSingleActivityIdToActivityIds(currentIds.get(0), targetIds).changeState();
}
// 如果父级任务只有一个,因此当前任务可能为网关中的任务
if (targetIds.size() == 1) {
// 1 对 1 或 多 对 1 情况currentIds 当前要跳转的节点列表(1或多)targetIds.get(0) 跳转到的节点(1)
runtimeService.createChangeActivityStateBuilder()
.processInstanceId(task.getProcessInstanceId())
.moveActivityIdsToSingleActivityId(currentIds, targetIds.get(0)).changeState();
}
} catch (FlowableObjectNotFoundException e) {
throw new RuntimeException("未找到流程实例,流程可能已发生变化");
} catch (FlowableException e) {
throw new RuntimeException("无法取消或开始活动");
}
// 设置任务节点名称
bo.setTaskName(task.getName());
if (!copyService.makeCopy(bo)) {
BpmnModel bpmnModel = repositoryService.getBpmnModel(processDefinition.getId());
EndEvent endEvent = ModelUtils.getEndEvent(bpmnModel);
// 终止流程
List<Execution> executions = runtimeService.createExecutionQuery().parentId(task.getProcessInstanceId()).list();
List<String> executionIds = executions.stream().map(Execution::getId).collect(Collectors.toList());
runtimeService.createChangeActivityStateBuilder()
.processInstanceId(task.getProcessInstanceId())
.moveExecutionsToSingleActivityId(executionIds, endEvent.getId())
.changeState();
// 处理抄送用户
if (!copyService.makeCopy(taskBo)) {
throw new RuntimeException("抄送任务失败");
}
}

View File

@@ -36,7 +36,7 @@ export function returnTask(data) {
})
}
// 驳回任务
// 拒绝任务
export function rejectTask(data) {
return request({
url: '/workflow/task/reject',

View File

@@ -52,7 +52,7 @@
<el-button icon="el-icon-refresh-left" type="warning" @click="handleReturn">退回</el-button>
</el-col>
<el-col :span="1.5">
<el-button icon="el-icon-circle-close" type="danger" @click="handleReject">驳回</el-button>
<el-button icon="el-icon-circle-close" type="danger" @click="handleReject">拒绝</el-button>
</el-col>
</el-row>
</el-card>
@@ -202,6 +202,7 @@ import { selectUser, deptTreeSelect } from '@/api/system/user'
import ProcessViewer from '@/components/ProcessViewer'
import '@riophae/vue-treeselect/dist/vue-treeselect.css'
import Treeselect from '@riophae/vue-treeselect'
import { forceLogout } from '@/api/monitor/online';
export default {
name: "Detail",
@@ -496,11 +497,14 @@ export default {
}
})
},
/** 驳回任务 */
/** 拒绝任务 */
handleReject() {
this.$refs["taskForm"].validate(valid => {
if (valid) {
rejectTask(this.taskForm).then(res => {
const _this = this;
this.$modal.confirm('拒绝审批单流程会终止,是否继续?').then(function() {
return rejectTask(_this.taskForm);
}).then(res => {
this.$modal.msgSuccess(res.msg);
this.goBack();
});