fix(流程审批): 移除流程驳回,新增审批拒绝,拒绝操作会直接终止流程。
This commit is contained in:
@@ -72,12 +72,12 @@ public class WfTaskController {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 驳回任务
|
* 拒绝任务
|
||||||
*/
|
*/
|
||||||
@PostMapping(value = "/reject")
|
@PostMapping(value = "/reject")
|
||||||
@SaCheckPermission("workflow:process:approval")
|
@SaCheckPermission("workflow:process:approval")
|
||||||
public R taskReject(@RequestBody WfTaskBo bo) {
|
public R taskReject(@RequestBody WfTaskBo taskBo) {
|
||||||
flowTaskService.taskReject(bo);
|
flowTaskService.taskReject(taskBo);
|
||||||
return R.ok();
|
return R.ok();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -66,6 +66,32 @@ public class ModelUtils {
|
|||||||
return null;
|
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;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 获取所有用户任务节点
|
* 获取所有用户任务节点
|
||||||
*
|
*
|
||||||
|
|||||||
@@ -24,11 +24,11 @@ public interface IWfTaskService {
|
|||||||
void complete(WfTaskBo task);
|
void complete(WfTaskBo task);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 驳回任务
|
* 拒绝任务
|
||||||
*
|
*
|
||||||
* @param bo
|
* @param taskBo
|
||||||
*/
|
*/
|
||||||
void taskReject(WfTaskBo bo);
|
void taskReject(WfTaskBo taskBo);
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|||||||
@@ -16,6 +16,7 @@ import com.ruoyi.flowable.factory.FlowServiceFactory;
|
|||||||
import com.ruoyi.flowable.flow.CustomProcessDiagramGenerator;
|
import com.ruoyi.flowable.flow.CustomProcessDiagramGenerator;
|
||||||
import com.ruoyi.flowable.flow.FindNextNodeUtil;
|
import com.ruoyi.flowable.flow.FindNextNodeUtil;
|
||||||
import com.ruoyi.flowable.flow.FlowableUtils;
|
import com.ruoyi.flowable.flow.FlowableUtils;
|
||||||
|
import com.ruoyi.flowable.utils.ModelUtils;
|
||||||
import com.ruoyi.flowable.utils.TaskUtils;
|
import com.ruoyi.flowable.utils.TaskUtils;
|
||||||
import com.ruoyi.system.service.ISysRoleService;
|
import com.ruoyi.system.service.ISysRoleService;
|
||||||
import com.ruoyi.system.service.ISysUserService;
|
import com.ruoyi.system.service.ISysUserService;
|
||||||
@@ -103,132 +104,48 @@ public class WfTaskServiceImpl extends FlowServiceFactory implements IWfTaskServ
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 驳回任务
|
* 拒绝任务
|
||||||
*
|
*
|
||||||
* @param bo
|
* @param taskBo
|
||||||
*/
|
*/
|
||||||
@Override
|
@Override
|
||||||
public void taskReject(WfTaskBo bo) {
|
@Transactional(rollbackFor = Exception.class)
|
||||||
|
public void taskReject(WfTaskBo taskBo) {
|
||||||
// 当前任务 task
|
// 当前任务 task
|
||||||
Task task = taskService.createTaskQuery().taskId(bo.getTaskId()).singleResult();
|
Task task = taskService.createTaskQuery().taskId(taskBo.getTaskId()).singleResult();
|
||||||
if (ObjectUtil.isNull(task)) {
|
if (ObjectUtil.isNull(task)) {
|
||||||
throw new RuntimeException("获取任务信息异常!");
|
throw new RuntimeException("获取任务信息异常!");
|
||||||
}
|
}
|
||||||
if (task.isSuspended()) {
|
if (task.isSuspended()) {
|
||||||
throw new RuntimeException("任务处于挂起状态");
|
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);
|
BpmnModel bpmnModel = repositoryService.getBpmnModel(processDefinition.getId());
|
||||||
// 获取全部节点列表,包含子节点
|
EndEvent endEvent = ModelUtils.getEndEvent(bpmnModel);
|
||||||
Collection<FlowElement> allElements = FlowableUtils.getAllElements(process.getFlowElements(), null);
|
// 终止流程
|
||||||
// 获取当前任务节点元素
|
List<Execution> executions = runtimeService.createExecutionQuery().parentId(task.getProcessInstanceId()).list();
|
||||||
FlowElement source = null;
|
List<String> executionIds = executions.stream().map(Execution::getId).collect(Collectors.toList());
|
||||||
if (allElements != null) {
|
runtimeService.createChangeActivityStateBuilder()
|
||||||
for (FlowElement flowElement : allElements) {
|
.processInstanceId(task.getProcessInstanceId())
|
||||||
// 类型为用户节点
|
.moveExecutionsToSingleActivityId(executionIds, endEvent.getId())
|
||||||
if (flowElement.getId().equals(task.getTaskDefinitionKey())) {
|
.changeState();
|
||||||
// 获取节点信息
|
// 处理抄送用户
|
||||||
source = flowElement;
|
if (!copyService.makeCopy(taskBo)) {
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// 目的获取所有跳转到的节点 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)) {
|
|
||||||
throw new RuntimeException("抄送任务失败");
|
throw new RuntimeException("抄送任务失败");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -36,7 +36,7 @@ export function returnTask(data) {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
// 驳回任务
|
// 拒绝任务
|
||||||
export function rejectTask(data) {
|
export function rejectTask(data) {
|
||||||
return request({
|
return request({
|
||||||
url: '/workflow/task/reject',
|
url: '/workflow/task/reject',
|
||||||
|
|||||||
@@ -52,7 +52,7 @@
|
|||||||
<el-button icon="el-icon-refresh-left" type="warning" @click="handleReturn">退回</el-button>
|
<el-button icon="el-icon-refresh-left" type="warning" @click="handleReturn">退回</el-button>
|
||||||
</el-col>
|
</el-col>
|
||||||
<el-col :span="1.5">
|
<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-col>
|
||||||
</el-row>
|
</el-row>
|
||||||
</el-card>
|
</el-card>
|
||||||
@@ -202,6 +202,7 @@ import { selectUser, deptTreeSelect } from '@/api/system/user'
|
|||||||
import ProcessViewer from '@/components/ProcessViewer'
|
import ProcessViewer from '@/components/ProcessViewer'
|
||||||
import '@riophae/vue-treeselect/dist/vue-treeselect.css'
|
import '@riophae/vue-treeselect/dist/vue-treeselect.css'
|
||||||
import Treeselect from '@riophae/vue-treeselect'
|
import Treeselect from '@riophae/vue-treeselect'
|
||||||
|
import { forceLogout } from '@/api/monitor/online';
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
name: "Detail",
|
name: "Detail",
|
||||||
@@ -496,11 +497,14 @@ export default {
|
|||||||
}
|
}
|
||||||
})
|
})
|
||||||
},
|
},
|
||||||
/** 驳回任务 */
|
/** 拒绝任务 */
|
||||||
handleReject() {
|
handleReject() {
|
||||||
this.$refs["taskForm"].validate(valid => {
|
this.$refs["taskForm"].validate(valid => {
|
||||||
if (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.$modal.msgSuccess(res.msg);
|
||||||
this.goBack();
|
this.goBack();
|
||||||
});
|
});
|
||||||
|
|||||||
Reference in New Issue
Block a user