Merge remote-tracking branch 'origin/feature/bpm' into feature/bpm

This commit is contained in:
jason 2024-08-24 22:57:39 +08:00
commit 6217d5acb1
4 changed files with 61 additions and 53 deletions

View File

@ -1,17 +1,8 @@
package cn.iocoder.yudao.module.bpm.framework.flowable.core.behavior; package cn.iocoder.yudao.module.bpm.framework.flowable.core.behavior;
import cn.hutool.core.collection.CollUtil; import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.util.ObjectUtil;
import cn.hutool.core.util.RandomUtil; import cn.hutool.core.util.RandomUtil;
import cn.hutool.extra.spring.SpringUtil;
import cn.iocoder.yudao.module.bpm.controller.admin.task.vo.task.BpmTaskApproveReqVO;
import cn.iocoder.yudao.module.bpm.controller.admin.task.vo.task.BpmTaskRejectReqVO;
import cn.iocoder.yudao.module.bpm.enums.definition.BpmUserTaskApproveTypeEnum;
import cn.iocoder.yudao.module.bpm.enums.definition.BpmUserTaskAssignEmptyHandlerTypeEnum;
import cn.iocoder.yudao.module.bpm.enums.task.BpmReasonEnum;
import cn.iocoder.yudao.module.bpm.framework.flowable.core.candidate.BpmTaskCandidateInvoker; import cn.iocoder.yudao.module.bpm.framework.flowable.core.candidate.BpmTaskCandidateInvoker;
import cn.iocoder.yudao.module.bpm.framework.flowable.core.util.BpmnModelUtils;
import cn.iocoder.yudao.module.bpm.service.task.BpmTaskService;
import lombok.Setter; import lombok.Setter;
import lombok.extern.slf4j.Slf4j; import lombok.extern.slf4j.Slf4j;
import org.flowable.bpmn.model.UserTask; import org.flowable.bpmn.model.UserTask;
@ -22,8 +13,7 @@ import org.flowable.engine.impl.cfg.ProcessEngineConfigurationImpl;
import org.flowable.engine.impl.util.TaskHelper; import org.flowable.engine.impl.util.TaskHelper;
import org.flowable.task.service.TaskService; import org.flowable.task.service.TaskService;
import org.flowable.task.service.impl.persistence.entity.TaskEntity; import org.flowable.task.service.impl.persistence.entity.TaskEntity;
import org.springframework.transaction.support.TransactionSynchronization; import org.springframework.transaction.annotation.Transactional;
import org.springframework.transaction.support.TransactionSynchronizationManager;
import java.util.List; import java.util.List;
import java.util.Set; import java.util.Set;
@ -46,6 +36,7 @@ public class BpmUserTaskActivityBehavior extends UserTaskActivityBehavior {
} }
@Override @Override
@Transactional(rollbackFor = Exception.class)
protected void handleAssignments(TaskService taskService, String assignee, String owner, protected void handleAssignments(TaskService taskService, String assignee, String owner,
List<String> candidateUsers, List<String> candidateGroups, TaskEntity task, ExpressionManager expressionManager, List<String> candidateUsers, List<String> candidateGroups, TaskEntity task, ExpressionManager expressionManager,
DelegateExecution execution, ProcessEngineConfigurationImpl processEngineConfiguration) { DelegateExecution execution, ProcessEngineConfigurationImpl processEngineConfiguration) {
@ -54,39 +45,7 @@ public class BpmUserTaskActivityBehavior extends UserTaskActivityBehavior {
// 第二步设置作为负责人 // 第二步设置作为负责人
if (assigneeUserId != null) { if (assigneeUserId != null) {
TaskHelper.changeTaskAssignee(task, String.valueOf(assigneeUserId)); TaskHelper.changeTaskAssignee(task, String.valueOf(assigneeUserId));
return;
} }
// 特殊处理需要自动通过不通过的情况
Integer approveType = BpmnModelUtils.parseApproveType(userTask);
Integer assignEmptyHandlerType = BpmnModelUtils.parseAssignEmptyHandlerType(userTask);
TransactionSynchronizationManager.registerSynchronization(new TransactionSynchronization() {
@Override
public void afterCommit() {
// 特殊情况一人工审核审批人为空根据配置是否要自动通过自动拒绝
if (ObjectUtil.equal(approveType, BpmUserTaskApproveTypeEnum.USER.getType())) {
if (ObjectUtil.equal(assignEmptyHandlerType, BpmUserTaskAssignEmptyHandlerTypeEnum.APPROVE.getType())) {
SpringUtil.getBean(BpmTaskService.class).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()
.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()
.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()
.setId(task.getId()).setReason(BpmReasonEnum.APPROVE_TYPE_AUTO_REJECT.getReason()));
}
}
}
});
} }
private Long calculateTaskCandidateUsers(DelegateExecution execution) { private Long calculateTaskCandidateUsers(DelegateExecution execution) {

View File

@ -30,10 +30,11 @@ public class BpmnVariableConstants {
*/ */
public static final String PROCESS_INSTANCE_VARIABLE_START_USER_SELECT_ASSIGNEES = "PROCESS_START_USER_SELECT_ASSIGNEES"; public static final String PROCESS_INSTANCE_VARIABLE_START_USER_SELECT_ASSIGNEES = "PROCESS_START_USER_SELECT_ASSIGNEES";
// TODO @芋艿用于处理驳回到发起人时如果被自动通过的逻辑
/** /**
* 流程实例的变量 - 用于判断流程实例变量节点是否驳回. 格式 RETURN_FLAG_{节点 id} * 流程实例的变量 - 用于判断流程实例变量节点是否驳回. 格式 RETURN_FLAG_{节点 id}
* *
* 目的是驳回到发起节点时因为审批人与发起人相同所以被自动通过但是此时还是希望不要自动通过
*
* @see ProcessInstance#getProcessVariables() * @see ProcessInstance#getProcessVariables()
*/ */
public static final String PROCESS_INSTANCE_VARIABLE_RETURN_FLAG = "RETURN_FLAG_%s"; public static final String PROCESS_INSTANCE_VARIABLE_RETURN_FLAG = "RETURN_FLAG_%s";

View File

@ -196,7 +196,12 @@ public interface BpmTaskService {
// ========== Event 事件相关方法 ========== // ========== Event 事件相关方法 ==========
/** /**
* 处理 Task 创建事件目前是更新它的状态为审批中 * 处理 Task 创建事件目前是
*
* 1. 更新它的状态为审批中
* 2. 处理自动通过的情况例如说1无审批人时是否自动通过不通过2人工审核是否自动通过不通过
*
* 注意它的触发时机晚于 {@link #processTaskAssigned(Task)} 之后
* *
* @param task 任务实体 * @param task 任务实体
*/ */

View File

@ -12,9 +12,7 @@ import cn.iocoder.yudao.framework.common.util.object.PageUtils;
import cn.iocoder.yudao.framework.web.core.util.WebFrameworkUtils; import cn.iocoder.yudao.framework.web.core.util.WebFrameworkUtils;
import cn.iocoder.yudao.module.bpm.controller.admin.task.vo.task.*; import cn.iocoder.yudao.module.bpm.controller.admin.task.vo.task.*;
import cn.iocoder.yudao.module.bpm.convert.task.BpmTaskConvert; import cn.iocoder.yudao.module.bpm.convert.task.BpmTaskConvert;
import cn.iocoder.yudao.module.bpm.enums.definition.BpmUserTaskAssignStartUserHandlerTypeEnum; import cn.iocoder.yudao.module.bpm.enums.definition.*;
import cn.iocoder.yudao.module.bpm.enums.definition.BpmUserTaskRejectHandlerType;
import cn.iocoder.yudao.module.bpm.enums.definition.BpmUserTaskTimeoutHandlerTypeEnum;
import cn.iocoder.yudao.module.bpm.enums.task.BpmCommentTypeEnum; import cn.iocoder.yudao.module.bpm.enums.task.BpmCommentTypeEnum;
import cn.iocoder.yudao.module.bpm.enums.task.BpmReasonEnum; import cn.iocoder.yudao.module.bpm.enums.task.BpmReasonEnum;
import cn.iocoder.yudao.module.bpm.enums.task.BpmTaskSignTypeEnum; import cn.iocoder.yudao.module.bpm.enums.task.BpmTaskSignTypeEnum;
@ -620,10 +618,11 @@ public class BpmTaskServiceImpl implements BpmTaskService {
updateTaskStatusAndReason(task.getId(), BpmTaskStatusEnum.RETURN.getStatus(), reqVO.getReason()); updateTaskStatusAndReason(task.getId(), BpmTaskStatusEnum.RETURN.getStatus(), reqVO.getReason());
}); });
// 设置流程变量节点驳回标记用于驳回到节点不执行 BpmUserTaskAssignStartUserHandlerTypeEnum 策略 自动通过 // 3. 设置流程变量节点驳回标记用于驳回到节点不执行 BpmUserTaskAssignStartUserHandlerTypeEnum 策略导致自动通过
runtimeService.setVariable(currentTask.getProcessInstanceId(), runtimeService.setVariable(currentTask.getProcessInstanceId(),
String.format(PROCESS_INSTANCE_VARIABLE_RETURN_FLAG, reqVO.getTargetTaskDefinitionKey()), Boolean.TRUE); String.format(PROCESS_INSTANCE_VARIABLE_RETURN_FLAG, reqVO.getTargetTaskDefinitionKey()), Boolean.TRUE);
// 3. 执行驳回
// 4. 执行驳回
runtimeService.createChangeActivityStateBuilder() runtimeService.createChangeActivityStateBuilder()
.processInstanceId(currentTask.getProcessInstanceId()) .processInstanceId(currentTask.getProcessInstanceId())
.moveActivityIdsToSingleActivityId(returnTaskKeyList, // 当前要跳转的节点列表( 1 或多) .moveActivityIdsToSingleActivityId(returnTaskKeyList, // 当前要跳转的节点列表( 1 或多)
@ -889,12 +888,55 @@ public class BpmTaskServiceImpl implements BpmTaskService {
@Override @Override
public void processTaskCreated(Task task) { public void processTaskCreated(Task task) {
// 1. 设置为待办中
Integer status = (Integer) task.getTaskLocalVariables().get(BpmnVariableConstants.TASK_VARIABLE_STATUS); Integer status = (Integer) task.getTaskLocalVariables().get(BpmnVariableConstants.TASK_VARIABLE_STATUS);
if (status != null) { if (status != null) {
log.error("[updateTaskStatusWhenCreated][taskId({}) 已经有状态({})]", task.getId(), status); log.error("[updateTaskStatusWhenCreated][taskId({}) 已经有状态({})]", task.getId(), status);
return; return;
} }
updateTaskStatus(task.getId(), BpmTaskStatusEnum.RUNNING.getStatus()); updateTaskStatus(task.getId(), BpmTaskStatusEnum.RUNNING.getStatus());
// 2. 处理自动通过的情况例如说1无审批人时是否自动通过不通过2人工审核是否自动通过不通过
ProcessInstance processInstance = processInstanceService.getProcessInstance(task.getProcessInstanceId());
if (processInstance == null) {
log.error("[processTaskCreated][taskId({}) 没有找到流程实例]", task.getId());
return;
}
BpmnModel bpmnModel = modelService.getBpmnModelByDefinitionId(processInstance.getProcessDefinitionId());
FlowElement userTaskElement = BpmnModelUtils.getFlowElementById(bpmnModel, task.getTaskDefinitionKey());
Integer approveType = BpmnModelUtils.parseApproveType(userTaskElement);
Integer assignEmptyHandlerType = BpmnModelUtils.parseAssignEmptyHandlerType(userTaskElement);
TransactionSynchronizationManager.registerSynchronization(new TransactionSynchronization() {
@Override
public void afterCompletion(int transactionStatus) {
// 特殊情况部分情况下TransactionSynchronizationManager 注册 afterCommit 监听时不会被调用但是 afterCompletion 可以
// 例如说第一个 task 就是配置自动通过或者自动拒绝
if (ObjectUtil.notEqual(transactionStatus, TransactionSynchronization.STATUS_COMMITTED)) {
return;
}
// 特殊情况一人工审核审批人为空根据配置是否要自动通过自动拒绝
if (ObjectUtil.equal(approveType, BpmUserTaskApproveTypeEnum.USER.getType())) {
if (ObjectUtil.equal(assignEmptyHandlerType, BpmUserTaskAssignEmptyHandlerTypeEnum.APPROVE.getType())) {
SpringUtil.getBean(BpmTaskService.class).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()
.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()
.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()
.setId(task.getId()).setReason(BpmReasonEnum.APPROVE_TYPE_AUTO_REJECT.getReason()));
}
}
}
});
} }
/** /**
@ -938,12 +980,13 @@ public class BpmTaskServiceImpl implements BpmTaskService {
log.error("[processTaskAssigned][taskId({}) 没有找到流程实例]", task.getId()); log.error("[processTaskAssigned][taskId({}) 没有找到流程实例]", task.getId());
return; return;
} }
// 审批人与提交人为同一人时根据策略进行处理 // 审批人与提交人为同一人时根据 BpmUserTaskAssignStartUserHandlerTypeEnum 策略进行处理
if (StrUtil.equals(task.getAssignee(), processInstance.getStartUserId())) { if (StrUtil.equals(task.getAssignee(), processInstance.getStartUserId())) {
// 判断是否为回退或者驳回 // 判断是否为回退或者驳回如果是回退或者驳回不走这个策略
// TODO 芋艿优化未来有没更好的判断方式另外还要考虑清理机制就是说下次处理了之后就移除这个标识
Boolean returnTaskFlag = runtimeService.getVariable(processInstance.getProcessInstanceId(), Boolean returnTaskFlag = runtimeService.getVariable(processInstance.getProcessInstanceId(),
String.format(PROCESS_INSTANCE_VARIABLE_RETURN_FLAG, task.getTaskDefinitionKey()), Boolean.class); String.format(PROCESS_INSTANCE_VARIABLE_RETURN_FLAG, task.getTaskDefinitionKey()), Boolean.class);
if (!BooleanUtil.isTrue(returnTaskFlag)) { // 如果是回退或者驳回不走这个策略 if (ObjUtil.notEqual(returnTaskFlag, Boolean.TRUE)) {
BpmnModel bpmnModel = modelService.getBpmnModelByDefinitionId(processInstance.getProcessDefinitionId()); BpmnModel bpmnModel = modelService.getBpmnModelByDefinitionId(processInstance.getProcessDefinitionId());
if (bpmnModel == null) { if (bpmnModel == null) {
log.error("[processTaskAssigned][taskId({}) 没有找到流程模型]", task.getId()); log.error("[processTaskAssigned][taskId({}) 没有找到流程模型]", task.getId());