houzhongjian
3 天以前 f4f9405f99cb35e2fd6cfeae4c54617304228fda
iailab-module-bpm/iailab-module-bpm-biz/src/main/java/com/iailab/module/bpm/service/task/BpmTaskServiceImpl.java
@@ -13,6 +13,7 @@
import com.iailab.framework.web.core.util.WebFrameworkUtils;
import com.iailab.module.bpm.controller.admin.task.vo.task.*;
import com.iailab.module.bpm.convert.task.BpmTaskConvert;
import com.iailab.module.bpm.dal.dataobject.definition.BpmFormDO;
import com.iailab.module.bpm.enums.definition.*;
import com.iailab.module.bpm.enums.task.BpmCommentTypeEnum;
import com.iailab.module.bpm.enums.task.BpmReasonEnum;
@@ -21,6 +22,7 @@
import com.iailab.module.bpm.framework.flowable.core.enums.BpmnVariableConstants;
import com.iailab.module.bpm.framework.flowable.core.util.BpmnModelUtils;
import com.iailab.module.bpm.framework.flowable.core.util.FlowableUtils;
import com.iailab.module.bpm.service.definition.BpmFormService;
import com.iailab.module.bpm.service.definition.BpmModelService;
import com.iailab.module.bpm.service.definition.BpmProcessDefinitionService;
import com.iailab.module.bpm.service.message.BpmMessageService;
@@ -92,6 +94,8 @@
    private BpmModelService modelService;
    @Resource
    private BpmMessageService messageService;
    @Resource
    private BpmFormService formService;
    @Resource
    private AdminUserApi adminUserApi;
@@ -109,6 +113,9 @@
                .orderByTaskCreateTime().desc(); // 创建时间倒序
        if (StrUtil.isNotBlank(pageVO.getName())) {
            taskQuery.taskNameLike("%" + pageVO.getName() + "%");
        }
        if (StrUtil.isNotEmpty(pageVO.getCategory())) {
            taskQuery.taskCategory(pageVO.getCategory());
        }
        if (ArrayUtil.isNotEmpty(pageVO.getCreateTime())) {
            taskQuery.taskCreatedAfter(DateUtils.of(pageVO.getCreateTime()[0]));
@@ -154,7 +161,13 @@
        BpmnModel bpmnModel = bpmProcessDefinitionService.getProcessDefinitionBpmnModel(todoTask.getProcessDefinitionId());
        Map<Integer, BpmTaskRespVO.OperationButtonSetting> buttonsSetting = BpmnModelUtils.parseButtonsSetting(
                bpmnModel, todoTask.getTaskDefinitionKey());
        return BpmTaskConvert.INSTANCE.buildTodoTask(todoTask, childrenTasks, buttonsSetting);
        // 4. 任务表单
        BpmFormDO taskForm = null;
        if (StrUtil.isNotBlank(todoTask.getFormKey())){
            taskForm = formService.getForm(NumberUtils.parseLong(todoTask.getFormKey()));
        }
        return BpmTaskConvert.INSTANCE.buildTodoTask(todoTask, childrenTasks, buttonsSetting, taskForm);
    }
    @Override
@@ -188,6 +201,9 @@
                .orderByHistoricTaskInstanceEndTime().desc(); // 审批时间倒序
        if (StrUtil.isNotBlank(pageVO.getName())) {
            taskQuery.taskNameLike("%" + pageVO.getName() + "%");
        }
        if (StrUtil.isNotEmpty(pageVO.getCategory())) {
            taskQuery.taskCategory(pageVO.getCategory());
        }
        if (ArrayUtil.isNotEmpty(pageVO.getCreateTime())) {
            taskQuery.taskCreatedAfter(DateUtils.of(pageVO.getCreateTime()[0]));
@@ -442,7 +458,7 @@
     * 判断指定用户,是否是当前任务的加签人
     *
     * @param userId 用户 Id
     * @param task 任务
     * @param task   任务
     * @return 是否
     */
    private boolean isAddSignUserTask(Long userId, Task task) {
@@ -670,7 +686,7 @@
                reqVO.getTargetTaskDefinitionKey(), task.getProcessDefinitionId());
        // 2. 调用 Flowable 框架的退回逻辑
        returnTask(task, targetElement, reqVO);
        returnTask(userId, task, targetElement, reqVO);
    }
    /**
@@ -702,11 +718,12 @@
    /**
     * 执行退回逻辑
     *
     * @param userId        用户编号
     * @param currentTask   当前退回的任务
     * @param targetElement 需要退回到的目标任务
     * @param reqVO         前端参数封装
     */
    public void returnTask(Task currentTask, FlowElement targetElement, BpmTaskReturnReqVO reqVO) {
    public void returnTask(Long userId, Task currentTask, FlowElement targetElement, BpmTaskReturnReqVO reqVO) {
        // 1. 获得所有需要回撤的任务 taskDefinitionKey,用于稍后的 moveActivityIdsToSingleActivityId 回撤
        // 1.1 获取所有正常进行的任务节点 Key
        List<Task> taskList = taskService.createTaskQuery().processInstanceId(currentTask.getProcessInstanceId()).list();
@@ -722,22 +739,29 @@
            if (!returnTaskKeyList.contains(task.getTaskDefinitionKey())) {
                return;
            }
            // 2.1 添加评论
            taskService.addComment(task.getId(), currentTask.getProcessInstanceId(), BpmCommentTypeEnum.RETURN.getType(),
                    BpmCommentTypeEnum.RETURN.formatComment(reqVO.getReason()));
            // 2.2 更新 task 状态 + 原因
            updateTaskStatusAndReason(task.getId(), BpmTaskStatusEnum.RETURN.getStatus(), reqVO.getReason());
            // 判断是否分配给自己任务,因为会签任务,一个节点会有多个任务
            if (isAssignUserTask(userId, task)) { // 情况一:自己的任务,进行 RETURN 标记
                // 2.1.1 添加评论
                taskService.addComment(task.getId(), currentTask.getProcessInstanceId(), BpmCommentTypeEnum.RETURN.getType(),
                        BpmCommentTypeEnum.RETURN.formatComment(reqVO.getReason()));
                // 2.1.2 更新 task 状态 + 原因
                updateTaskStatusAndReason(task.getId(), BpmTaskStatusEnum.RETURN.getStatus(), reqVO.getReason());
            } else { // 情况二:别人的任务,进行 CANCEL 标记
                processTaskCanceled(task.getId());
            }
        });
        // 3. 设置流程变量节点驳回标记:用于驳回到节点,不执行 BpmUserTaskAssignStartUserHandlerTypeEnum 策略。导致自动通过
        runtimeService.setVariable(currentTask.getProcessInstanceId(),
                String.format(PROCESS_INSTANCE_VARIABLE_RETURN_FLAG, reqVO.getTargetTaskDefinitionKey()), Boolean.TRUE);
        // 4. 执行驳回
        // 使用 moveExecutionsToSingleActivityId 替换 moveActivityIdsToSingleActivityId 原因:
        // 当多实例任务回退的时候有问题。相关 issue: https://github.com/flowable/flowable-engine/issues/3944
        List<String> runExecutionIds = convertList(taskList, Task::getExecutionId);
        runtimeService.createChangeActivityStateBuilder()
                .processInstanceId(currentTask.getProcessInstanceId())
                .moveActivityIdsToSingleActivityId(returnTaskKeyList, // 当前要跳转的节点列表( 1 或多)
                        reqVO.getTargetTaskDefinitionKey()) // targetKey 跳转到的节点(1)
                .moveExecutionsToSingleActivityId(runExecutionIds, reqVO.getTargetTaskDefinitionKey())
                .changeState();
    }
@@ -1022,14 +1046,22 @@
        Integer assignEmptyHandlerType = BpmnModelUtils.parseAssignEmptyHandlerType(userTaskElement);
        TransactionSynchronizationManager.registerSynchronization(new TransactionSynchronization() {
            /**
             * 特殊情况:部分情况下,TransactionSynchronizationManager 注册 afterCommit 监听时,不会被调用,但是 afterCompletion 可以
             * 例如说:第一个 task 就是配置【自动通过】或者【自动拒绝】时
             * 参见 <a href="https://gitee.com/zhijiantianya/yudao-cloud/issues/IB7V7Q">issue</a> 反馈
             */
            @Override
            public void afterCompletion(int transactionStatus) {
                // 特殊情况:部分情况下,TransactionSynchronizationManager 注册 afterCommit 监听时,不会被调用,但是 afterCompletion 可以
                // 例如说:第一个 task 就是配置【自动通过】或者【自动拒绝】时
                if (ObjectUtil.notEqual(transactionStatus, TransactionSynchronization.STATUS_COMMITTED)) {
                // 回滚情况,直接返回
                if (ObjectUtil.equal(transactionStatus, TransactionSynchronization.STATUS_ROLLED_BACK)) {
                    return;
                }
                // TODO 芋艿:可以后续优化成 getSelf();
                // 特殊情况:第一个 task 【自动通过】时,第二个任务设置审批人时 transactionStatus 会为 STATUS_UNKNOWN,不知道啥原因
                if (ObjectUtil.equal(transactionStatus, TransactionSynchronization.STATUS_UNKNOWN)
                        && getTask(task.getId()) == null) {
                    return;
                }
                // 特殊情况一:【人工审核】审批人为空,根据配置是否要自动通过、自动拒绝
                if (ObjectUtil.equal(approveType, BpmUserTaskApproveTypeEnum.USER.getType())) {
                    // 如果有审批人、或者拥有人,则说明不满足情况一,不自动通过、不自动拒绝
@@ -1037,19 +1069,19 @@
                        return;
                    }
                    if (ObjectUtil.equal(assignEmptyHandlerType, BpmUserTaskAssignEmptyHandlerTypeEnum.APPROVE.getType())) {
                        SpringUtil.getBean(BpmTaskService.class).approveTask(null, new BpmTaskApproveReqVO()
                        getSelf().approveTask(null, new BpmTaskApproveReqVO()
                                .setId(task.getId()).setReason(BpmReasonEnum.ASSIGN_EMPTY_APPROVE.getReason()));
                    } else if (ObjectUtil.equal(assignEmptyHandlerType, BpmUserTaskAssignEmptyHandlerTypeEnum.REJECT.getType())) {
                        SpringUtil.getBean(BpmTaskService.class).rejectTask(null, new BpmTaskRejectReqVO()
                        getSelf().rejectTask(null, new BpmTaskRejectReqVO()
                                .setId(task.getId()).setReason(BpmReasonEnum.ASSIGN_EMPTY_REJECT.getReason()));
                    }
                    // 特殊情况二:【自动审核】审批类型为自动通过、不通过
                } else {
                    if (ObjectUtil.equal(approveType, BpmUserTaskApproveTypeEnum.AUTO_APPROVE.getType())) {
                        SpringUtil.getBean(BpmTaskService.class).approveTask(null, new BpmTaskApproveReqVO()
                        getSelf().approveTask(null, new BpmTaskApproveReqVO()
                                .setId(task.getId()).setReason(BpmReasonEnum.APPROVE_TYPE_AUTO_APPROVE.getReason()));
                    } else if (ObjectUtil.equal(approveType, BpmUserTaskApproveTypeEnum.AUTO_REJECT.getType())) {
                        SpringUtil.getBean(BpmTaskService.class).rejectTask(null, new BpmTaskRejectReqVO()
                        getSelf().rejectTask(null, new BpmTaskRejectReqVO()
                                .setId(task.getId()).setReason(BpmReasonEnum.APPROVE_TYPE_AUTO_REJECT.getReason()));
                    }
                }
@@ -1088,8 +1120,22 @@
        // 发送通知。在事务提交时,批量执行操作,所以直接查询会无法查询到 ProcessInstance,所以这里是通过监听事务的提交来实现。
        TransactionSynchronizationManager.registerSynchronization(new TransactionSynchronization() {
            /**
             * 特殊情况:部分情况下,TransactionSynchronizationManager 注册 afterCommit 监听时,不会被调用,但是 afterCompletion 可以
             * 例如说:第一个 task 就是配置【自动通过】或者【自动拒绝】时
             * 参见 <a href="https://gitee.com/zhijiantianya/yudao-cloud/issues/IB7V7Q">issue</a> 反馈
             */
            @Override
            public void afterCommit() {
            public void afterCompletion(int transactionStatus) {
                // 回滚情况,直接返回
                if (ObjectUtil.equal(transactionStatus, TransactionSynchronization.STATUS_ROLLED_BACK)) {
                    return;
                }
                // 特殊情况:第一个 task 【自动通过】时,第二个任务设置审批人时 transactionStatus 会为 STATUS_UNKNOWN,不知道啥原因
                if (ObjectUtil.equal(transactionStatus, TransactionSynchronization.STATUS_UNKNOWN)
                        && getTask(task.getId()) == null) {
                    return;
                }
                if (StrUtil.isEmpty(task.getAssignee())) {
                    log.error("[processTaskAssigned][taskId({}) 没有分配到负责人]", task.getId());
                    return;
@@ -1146,6 +1192,7 @@
                        }
                    }
                }
                AdminUserRespDTO startUser = adminUserApi.getUser(Long.valueOf(processInstance.getStartUserId())).getCheckedData();
                messageService.sendMessageWhenTaskAssigned(BpmTaskConvert.INSTANCE.convert(processInstance, startUser, task));
            }