mirror of
https://gitee.com/huangge1199_admin/vue-pro.git
synced 2024-11-26 17:21:53 +08:00
Merge remote-tracking branch 'origin/feature/bpm' into feature/bpm
This commit is contained in:
commit
bc46c67a99
@ -13,8 +13,7 @@ import lombok.Getter;
|
||||
@AllArgsConstructor
|
||||
public enum BpmBoundaryEventType {
|
||||
|
||||
USER_TASK_TIMEOUT(1,"用户任务超时"),
|
||||
USER_TASK_REJECT_POST_PROCESS(2, "用户任务拒绝后处理");
|
||||
USER_TASK_TIMEOUT(1,"用户任务超时");
|
||||
|
||||
private final Integer type;
|
||||
private final String name;
|
||||
@ -22,4 +21,5 @@ public enum BpmBoundaryEventType {
|
||||
public static BpmBoundaryEventType typeOf(Integer type) {
|
||||
return ArrayUtil.firstMatch(eventType -> eventType.getType().equals(type), values());
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -0,0 +1,27 @@
|
||||
package cn.iocoder.yudao.module.bpm.enums.definition;
|
||||
|
||||
import cn.iocoder.yudao.framework.common.core.IntArrayValuable;
|
||||
import lombok.Getter;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
|
||||
/**
|
||||
* BPM 用户任务的审批人与发起人相同时,处理类型枚举
|
||||
*
|
||||
* @author 芋道源码
|
||||
*/
|
||||
@RequiredArgsConstructor
|
||||
@Getter
|
||||
public enum BpmUserTaskAssignStartUserHandlerTypeEnum implements IntArrayValuable {
|
||||
|
||||
START_USER_AUDIT(1), // 由发起人对自己审批
|
||||
SKIP(2), // 自动跳过【参考飞书】:1)如果当前节点还有其他审批人,则交由其他审批人进行审批;2)如果当前节点没有其他审批人,则该节点自动通过
|
||||
ASSIGN_DEPT_LEADER(3); // 转交给部门负责人审批【参考飞书】:若部门负责人为空,则自动通过
|
||||
|
||||
private final Integer type;
|
||||
|
||||
@Override
|
||||
public int[] array() {
|
||||
return new int[0];
|
||||
}
|
||||
|
||||
}
|
@ -20,6 +20,7 @@ public enum BpmUserTaskTimeoutHandlerType implements IntArrayValuable {
|
||||
APPROVE(2, "自动同意"),
|
||||
REJECT(3, "自动拒绝");
|
||||
|
||||
// TODO @jason:type 是不是更合适哈;
|
||||
private final Integer action;
|
||||
private final String name;
|
||||
|
||||
|
@ -14,7 +14,8 @@ public enum BpmMessageEnum {
|
||||
|
||||
PROCESS_INSTANCE_APPROVE("bpm_process_instance_approve"), // 流程任务被审批通过时,发送给申请人
|
||||
PROCESS_INSTANCE_REJECT("bpm_process_instance_reject"), // 流程任务被审批不通过时,发送给申请人
|
||||
TASK_ASSIGNED("bpm_task_assigned"); // 任务被分配时,发送给审批人
|
||||
TASK_ASSIGNED("bpm_task_assigned"), // 任务被分配时,发送给审批人
|
||||
TASK_TIMEOUT("bpm_task_timeout"); // 任务审批超时时,发送给审批人
|
||||
|
||||
/**
|
||||
* 短信模板的标识
|
||||
|
@ -1,10 +1,7 @@
|
||||
package cn.iocoder.yudao.module.bpm.controller.admin.definition.vo.model.simple;
|
||||
|
||||
import cn.iocoder.yudao.framework.common.validation.InEnum;
|
||||
import cn.iocoder.yudao.module.bpm.enums.definition.BpmApproveMethodEnum;
|
||||
import cn.iocoder.yudao.module.bpm.enums.definition.BpmSimpleModelNodeType;
|
||||
import cn.iocoder.yudao.module.bpm.enums.definition.BpmUserTaskRejectHandlerType;
|
||||
import cn.iocoder.yudao.module.bpm.enums.definition.BpmUserTaskTimeoutHandlerType;
|
||||
import cn.iocoder.yudao.module.bpm.enums.definition.*;
|
||||
import cn.iocoder.yudao.module.bpm.framework.flowable.core.enums.BpmTaskCandidateStrategyEnum;
|
||||
import com.fasterxml.jackson.annotation.JsonIgnore;
|
||||
import com.fasterxml.jackson.annotation.JsonInclude;
|
||||
@ -84,6 +81,10 @@ public class BpmSimpleModelNodeVO {
|
||||
*/
|
||||
private TimeoutHandler timeoutHandler;
|
||||
|
||||
@Schema(description = "审批节点的审批人与发起人相同时,对应的处理类型", example = "1")
|
||||
@InEnum(BpmUserTaskAssignStartUserHandlerTypeEnum.class)
|
||||
private Integer assignStartUserHandlerType;
|
||||
|
||||
@Data
|
||||
@Schema(description = "审批节点拒绝处理策略")
|
||||
public static class RejectHandler {
|
||||
@ -96,6 +97,7 @@ public class BpmSimpleModelNodeVO {
|
||||
private String returnNodeId;
|
||||
}
|
||||
|
||||
// TODO @芋艿:参数校验
|
||||
@Data
|
||||
@Schema(description = "审批节点超时处理策略")
|
||||
public static class TimeoutHandler {
|
||||
@ -103,6 +105,7 @@ public class BpmSimpleModelNodeVO {
|
||||
@Schema(description = "是否开启超时处理", example = "false")
|
||||
private Boolean enable;
|
||||
|
||||
// TODO @jason:type 是不是更合适哈;
|
||||
@Schema(description = "任务超时未处理的行为", example = "1")
|
||||
@InEnum(BpmUserTaskTimeoutHandlerType.class)
|
||||
private Integer action;
|
||||
@ -112,6 +115,7 @@ public class BpmSimpleModelNodeVO {
|
||||
|
||||
@Schema(description = "最大提醒次数", example = "1")
|
||||
private Integer maxRemindCount;
|
||||
|
||||
}
|
||||
|
||||
@Data
|
||||
@ -129,14 +133,7 @@ public class BpmSimpleModelNodeVO {
|
||||
}
|
||||
|
||||
// Map<String, Integer> formPermissions; 表单权限;仅发起、审批、抄送节点会使用
|
||||
// Integer approveMethod; 审批方式;仅审批节点会使用
|
||||
// TODO @jason 后面和前端一起调整一下;下面的 ①、②、③ 是优先级
|
||||
// TODO @芋艿:① 审批人的选择;
|
||||
// TODO @芋艿:⑥ 没有人的策略?
|
||||
// TODO @芋艿:② 审批拒绝的策略?
|
||||
// TODO @芋艿:③ 配置的可操作列表?(操作权限)
|
||||
// TODO @芋艿:④ 表单的权限列表?
|
||||
// TODO @芋艿:⑨ 超时配置;要支持指定时间点、指定时间间隔;
|
||||
// TODO @芋艿:条件;建议可以固化的一些选项;然后有个表达式兜底;要支持
|
||||
|
||||
}
|
||||
|
@ -176,7 +176,6 @@ public interface BpmTaskConvert {
|
||||
childTask.setParentTaskId(parentTask.getId());
|
||||
childTask.setProcessDefinitionId(parentTask.getProcessDefinitionId());
|
||||
childTask.setProcessInstanceId(parentTask.getProcessInstanceId());
|
||||
// childTask.setExecutionId(parentTask.getExecutionId()); // TODO 芋艿:新加的,不太确定;尴尬,不加时,子任务不通过会失败(报错);加了,子任务审批通过会失败(报错)
|
||||
childTask.setTaskDefinitionKey(parentTask.getTaskDefinitionKey());
|
||||
childTask.setTaskDefinitionId(parentTask.getTaskDefinitionId());
|
||||
childTask.setPriority(parentTask.getPriority());
|
||||
|
@ -2,11 +2,15 @@ package cn.iocoder.yudao.module.bpm.framework.flowable.core.candidate;
|
||||
|
||||
import cn.hutool.core.collection.CollUtil;
|
||||
import cn.hutool.core.lang.Assert;
|
||||
import cn.hutool.core.util.ObjectUtil;
|
||||
import cn.hutool.core.util.StrUtil;
|
||||
import cn.hutool.extra.spring.SpringUtil;
|
||||
import cn.iocoder.yudao.framework.common.enums.CommonStatusEnum;
|
||||
import cn.iocoder.yudao.framework.datapermission.core.annotation.DataPermission;
|
||||
import cn.iocoder.yudao.module.bpm.enums.definition.BpmUserTaskAssignStartUserHandlerTypeEnum;
|
||||
import cn.iocoder.yudao.module.bpm.framework.flowable.core.enums.BpmTaskCandidateStrategyEnum;
|
||||
import cn.iocoder.yudao.module.bpm.framework.flowable.core.util.BpmnModelUtils;
|
||||
import cn.iocoder.yudao.module.bpm.service.task.BpmProcessInstanceService;
|
||||
import cn.iocoder.yudao.module.system.api.user.AdminUserApi;
|
||||
import cn.iocoder.yudao.module.system.api.user.dto.AdminUserRespDTO;
|
||||
import com.google.common.annotations.VisibleForTesting;
|
||||
@ -14,6 +18,7 @@ import lombok.extern.slf4j.Slf4j;
|
||||
import org.flowable.bpmn.model.BpmnModel;
|
||||
import org.flowable.bpmn.model.UserTask;
|
||||
import org.flowable.engine.delegate.DelegateExecution;
|
||||
import org.flowable.engine.runtime.ProcessInstance;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
@ -86,6 +91,8 @@ public class BpmTaskCandidateInvoker {
|
||||
Set<Long> userIds = getCandidateStrategy(strategy).calculateUsers(execution, param);
|
||||
// 1.2 移除被禁用的用户
|
||||
removeDisableUsers(userIds);
|
||||
// 1.3 移除发起人的用户
|
||||
removeStartUserIfSkip(execution, userIds);
|
||||
|
||||
// 2. 校验是否有候选人
|
||||
if (CollUtil.isEmpty(userIds)) {
|
||||
@ -108,6 +115,29 @@ public class BpmTaskCandidateInvoker {
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* 如果“审批人与发起人相同时”,配置了 SKIP 跳过,则移除发起人
|
||||
*
|
||||
* 注意:如果只有一个候选人,则不处理,避免无法审批
|
||||
*
|
||||
* @param execution 执行中的任务
|
||||
* @param assigneeUserIds 当前分配的候选人
|
||||
*/
|
||||
@VisibleForTesting
|
||||
void removeStartUserIfSkip(DelegateExecution execution, Set<Long> assigneeUserIds) {
|
||||
if (CollUtil.size(assigneeUserIds) <= 1) {
|
||||
return;
|
||||
}
|
||||
Integer assignStartUserHandlerType = BpmnModelUtils.parseAssignStartUserHandlerType(execution.getCurrentFlowElement());
|
||||
if (ObjectUtil.notEqual(assignStartUserHandlerType, BpmUserTaskAssignStartUserHandlerTypeEnum.SKIP.getType())) {
|
||||
return;
|
||||
}
|
||||
ProcessInstance processInstance = SpringUtil.getBean(BpmProcessInstanceService.class)
|
||||
.getProcessInstance(execution.getProcessInstanceId());
|
||||
Assert.notNull(processInstance, "流程实例({}) 不存在", execution.getProcessInstanceId());
|
||||
assigneeUserIds.remove(Long.valueOf(processInstance.getStartUserId()));
|
||||
}
|
||||
|
||||
private BpmTaskCandidateStrategy getCandidateStrategy(Integer strategy) {
|
||||
BpmTaskCandidateStrategyEnum strategyEnum = BpmTaskCandidateStrategyEnum.valueOf(strategy);
|
||||
Assert.notNull(strategyEnum, "策略(%s) 不存在", strategy);
|
||||
|
@ -29,12 +29,17 @@ public interface BpmnModelConstants {
|
||||
String BOUNDARY_EVENT_TYPE = "boundaryEventType";
|
||||
|
||||
// TODO @jason:这个命名,应该也要改哈
|
||||
// TODO @jason:1)是不是上面的 timeoutAction 改成 timeoutHandler;
|
||||
/**
|
||||
* BPMN ExtensionElement 的扩展属性,用于标记用户任务超时执行动作
|
||||
*/
|
||||
String USER_TASK_TIMEOUT_HANDLER_ACTION = "timeoutAction";
|
||||
|
||||
// TODO @jason:1)是不是上面的 timeoutAction 改成 timeoutHandler;2)rejectHandlerType 改成 rejectHandler 哇?
|
||||
/**
|
||||
* BPMN ExtensionElement 的扩展属性,用于标记用户任务的审批人与发起人相同时,对应的处理类型
|
||||
*/
|
||||
String USER_TASK_ASSIGN_START_USER_HANDLER_TYPE = "assignStartUserHandlerType";
|
||||
|
||||
/**
|
||||
* BPMN ExtensionElement 的扩展属性,用于标记用户任务拒绝处理类型
|
||||
*/
|
||||
|
@ -1,17 +1,27 @@
|
||||
package cn.iocoder.yudao.module.bpm.framework.flowable.core.listener;
|
||||
|
||||
import cn.hutool.core.collection.CollUtil;
|
||||
import cn.hutool.core.util.ObjectUtil;
|
||||
import cn.hutool.core.util.StrUtil;
|
||||
import cn.iocoder.yudao.framework.common.util.number.NumberUtils;
|
||||
import cn.iocoder.yudao.module.bpm.enums.definition.BpmBoundaryEventType;
|
||||
import cn.iocoder.yudao.module.bpm.framework.flowable.core.enums.BpmnModelConstants;
|
||||
import cn.iocoder.yudao.module.bpm.framework.flowable.core.util.BpmnModelUtils;
|
||||
import cn.iocoder.yudao.module.bpm.service.definition.BpmModelService;
|
||||
import cn.iocoder.yudao.module.bpm.service.task.BpmActivityService;
|
||||
import cn.iocoder.yudao.module.bpm.service.task.BpmTaskService;
|
||||
import com.google.common.collect.ImmutableSet;
|
||||
import jakarta.annotation.Resource;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.flowable.bpmn.model.BoundaryEvent;
|
||||
import org.flowable.bpmn.model.BpmnModel;
|
||||
import org.flowable.bpmn.model.FlowElement;
|
||||
import org.flowable.common.engine.api.delegate.event.FlowableEngineEntityEvent;
|
||||
import org.flowable.common.engine.api.delegate.event.FlowableEngineEventType;
|
||||
import org.flowable.engine.delegate.event.AbstractFlowableEngineEventListener;
|
||||
import org.flowable.engine.delegate.event.FlowableActivityCancelledEvent;
|
||||
import org.flowable.engine.history.HistoricActivityInstance;
|
||||
import org.flowable.job.api.Job;
|
||||
import org.flowable.task.api.Task;
|
||||
import org.springframework.context.annotation.Lazy;
|
||||
import org.springframework.stereotype.Component;
|
||||
@ -28,6 +38,9 @@ import java.util.Set;
|
||||
@Slf4j
|
||||
public class BpmTaskEventListener extends AbstractFlowableEngineEventListener {
|
||||
|
||||
@Resource
|
||||
@Lazy // 延迟加载,避免循环依赖
|
||||
private BpmModelService modelService;
|
||||
@Resource
|
||||
@Lazy // 解决循环依赖
|
||||
private BpmTaskService taskService;
|
||||
@ -40,6 +53,7 @@ public class BpmTaskEventListener extends AbstractFlowableEngineEventListener {
|
||||
.add(FlowableEngineEventType.TASK_ASSIGNED)
|
||||
// .add(FlowableEngineEventType.TASK_COMPLETED) // 由于审批通过时,已经记录了 task 的 status 为通过,所以不需要监听了。
|
||||
.add(FlowableEngineEventType.ACTIVITY_CANCELLED)
|
||||
.add(FlowableEngineEventType.TIMER_FIRED) // 监听审批超时
|
||||
.build();
|
||||
|
||||
public BpmTaskEventListener() {
|
||||
@ -72,4 +86,30 @@ public class BpmTaskEventListener extends AbstractFlowableEngineEventListener {
|
||||
});
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void timerFired(FlowableEngineEntityEvent event) {
|
||||
// 1.1 只处理 BoundaryEvent 边界计时时间
|
||||
String processDefinitionId = event.getProcessDefinitionId();
|
||||
BpmnModel bpmnModel = modelService.getBpmnModelByDefinitionId(processDefinitionId);
|
||||
Job entity = (Job) event.getEntity();
|
||||
FlowElement element = BpmnModelUtils.getFlowElementById(bpmnModel, entity.getElementId());
|
||||
if (!(element instanceof BoundaryEvent)) {
|
||||
return;
|
||||
}
|
||||
// 1.2 判断是否为超时处理
|
||||
BoundaryEvent boundaryEvent = (BoundaryEvent) element;
|
||||
String boundaryEventType = BpmnModelUtils.parseBoundaryEventExtensionElement(boundaryEvent,
|
||||
BpmnModelConstants.BOUNDARY_EVENT_TYPE);
|
||||
BpmBoundaryEventType bpmTimerBoundaryEventType = BpmBoundaryEventType.typeOf(NumberUtils.parseInt(boundaryEventType));
|
||||
if (ObjectUtil.notEqual(bpmTimerBoundaryEventType, BpmBoundaryEventType.USER_TASK_TIMEOUT)) {
|
||||
return;
|
||||
}
|
||||
|
||||
// 2. 处理超时
|
||||
String timeoutAction = BpmnModelUtils.parseBoundaryEventExtensionElement(boundaryEvent,
|
||||
BpmnModelConstants.USER_TASK_TIMEOUT_HANDLER_ACTION);
|
||||
String taskKey = boundaryEvent.getAttachedToRefId();
|
||||
taskService.processTaskTimeout(event.getProcessInstanceId(), taskKey, NumberUtils.parseInt(timeoutAction));
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -1,111 +0,0 @@
|
||||
package cn.iocoder.yudao.module.bpm.framework.flowable.core.listener;
|
||||
|
||||
import cn.iocoder.yudao.framework.common.util.number.NumberUtils;
|
||||
import cn.iocoder.yudao.framework.tenant.core.context.TenantContextHolder;
|
||||
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.BpmBoundaryEventType;
|
||||
import cn.iocoder.yudao.module.bpm.enums.definition.BpmUserTaskTimeoutHandlerType;
|
||||
import cn.iocoder.yudao.module.bpm.framework.flowable.core.enums.BpmnModelConstants;
|
||||
import cn.iocoder.yudao.module.bpm.framework.flowable.core.mq.message.task.TodoTaskReminderMessage;
|
||||
import cn.iocoder.yudao.module.bpm.framework.flowable.core.mq.producer.task.TodoTaskReminderProducer;
|
||||
import cn.iocoder.yudao.module.bpm.framework.flowable.core.util.BpmnModelUtils;
|
||||
import cn.iocoder.yudao.module.bpm.service.definition.BpmModelService;
|
||||
import cn.iocoder.yudao.module.bpm.service.task.BpmTaskService;
|
||||
import com.google.common.collect.ImmutableSet;
|
||||
import jakarta.annotation.Resource;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.flowable.bpmn.model.BoundaryEvent;
|
||||
import org.flowable.bpmn.model.BpmnModel;
|
||||
import org.flowable.bpmn.model.FlowElement;
|
||||
import org.flowable.common.engine.api.delegate.event.FlowableEngineEntityEvent;
|
||||
import org.flowable.common.engine.api.delegate.event.FlowableEngineEventType;
|
||||
import org.flowable.engine.delegate.event.AbstractFlowableEngineEventListener;
|
||||
import org.flowable.job.api.Job;
|
||||
import org.flowable.task.api.Task;
|
||||
import org.springframework.context.annotation.Lazy;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
|
||||
// TODO @芋艿:这块需要仔细再瞅瞅
|
||||
/**
|
||||
* 监听定时器触发事件
|
||||
*
|
||||
* @author jason
|
||||
*/
|
||||
@Component
|
||||
@Slf4j
|
||||
public class BpmTimerFiredEventListener extends AbstractFlowableEngineEventListener {
|
||||
|
||||
@Resource
|
||||
@Lazy // 延迟加载,避免循环依赖
|
||||
private BpmModelService bpmModelService;
|
||||
@Resource
|
||||
@Lazy // 延迟加载,避免循环依赖
|
||||
private BpmTaskService bpmTaskService;
|
||||
|
||||
@Resource
|
||||
private TodoTaskReminderProducer todoTaskReminderProducer;
|
||||
|
||||
public static final Set<FlowableEngineEventType> TIME_EVENTS = ImmutableSet.<FlowableEngineEventType>builder()
|
||||
.add(FlowableEngineEventType.TIMER_FIRED)
|
||||
.build();
|
||||
|
||||
public BpmTimerFiredEventListener() {
|
||||
super(TIME_EVENTS);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void timerFired(FlowableEngineEntityEvent event) {
|
||||
String processDefinitionId = event.getProcessDefinitionId();
|
||||
BpmnModel bpmnModel = bpmModelService.getBpmnModelByDefinitionId(processDefinitionId);
|
||||
Job entity = (Job) event.getEntity();
|
||||
FlowElement element = BpmnModelUtils.getFlowElementById(bpmnModel, entity.getElementId());
|
||||
// 如果是定时器边界事件
|
||||
if (element instanceof BoundaryEvent) {
|
||||
BoundaryEvent boundaryEvent = (BoundaryEvent) element;
|
||||
String boundaryEventType = BpmnModelUtils.parseBoundaryEventExtensionElement(boundaryEvent, BpmnModelConstants.BOUNDARY_EVENT_TYPE);
|
||||
BpmBoundaryEventType bpmTimerBoundaryEventType = BpmBoundaryEventType.typeOf(NumberUtils.parseInt(boundaryEventType));
|
||||
// 类型为用户任务超时未处理的情况
|
||||
if (bpmTimerBoundaryEventType == BpmBoundaryEventType.USER_TASK_TIMEOUT) {
|
||||
String timeoutAction = BpmnModelUtils.parseBoundaryEventExtensionElement(boundaryEvent, BpmnModelConstants.USER_TASK_TIMEOUT_HANDLER_ACTION);
|
||||
userTaskTimeoutHandler(event.getProcessInstanceId(), boundaryEvent.getAttachedToRefId(), NumberUtils.parseInt(timeoutAction));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void userTaskTimeoutHandler(String processInstanceId, String taskDefKey, Integer timeoutAction) {
|
||||
BpmUserTaskTimeoutHandlerType userTaskTimeoutAction = BpmUserTaskTimeoutHandlerType.typeOf(timeoutAction);
|
||||
if (userTaskTimeoutAction != null) {
|
||||
// 查询超时未处理的任务 TODO 加签的情况会不会有问题 ???
|
||||
List<Task> taskList = bpmTaskService.getRunningTaskListByProcessInstanceId(processInstanceId, true, taskDefKey);
|
||||
taskList.forEach(task -> {
|
||||
// 自动提醒
|
||||
if (userTaskTimeoutAction == BpmUserTaskTimeoutHandlerType.REMINDER) {
|
||||
TodoTaskReminderMessage message = new TodoTaskReminderMessage().setTenantId(Long.parseLong(task.getTenantId()))
|
||||
.setUserId(Long.parseLong(task.getAssignee())).setTaskName(task.getName());
|
||||
todoTaskReminderProducer.sendReminderMessage(message);
|
||||
}
|
||||
// 自动同意
|
||||
if (userTaskTimeoutAction == BpmUserTaskTimeoutHandlerType.APPROVE) {
|
||||
// TODO @芋艿 这个上下文如何清除呢? 任务通过后, BpmProcessInstanceEventListener 会有回调
|
||||
TenantContextHolder.setTenantId(Long.parseLong(task.getTenantId()));
|
||||
TenantContextHolder.setIgnore(false);
|
||||
BpmTaskApproveReqVO req = new BpmTaskApproveReqVO().setId(task.getId())
|
||||
.setReason("超时系统自动同意");
|
||||
bpmTaskService.approveTask(Long.parseLong(task.getAssignee()), req);
|
||||
}
|
||||
// 自动拒绝
|
||||
if (userTaskTimeoutAction == BpmUserTaskTimeoutHandlerType.REJECT) {
|
||||
// TODO @芋艿 这个上下文如何清除呢? 任务拒绝后, BpmProcessInstanceEventListener 会有回调
|
||||
TenantContextHolder.setTenantId(Long.parseLong(task.getTenantId()));
|
||||
TenantContextHolder.setIgnore(false);
|
||||
BpmTaskRejectReqVO req = new BpmTaskRejectReqVO().setId(task.getId()).setReason("超时系统自动拒绝");
|
||||
bpmTaskService.rejectTask(Long.parseLong(task.getAssignee()), req);
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
@ -1,42 +0,0 @@
|
||||
package cn.iocoder.yudao.module.bpm.framework.flowable.core.mq.consumer.task;
|
||||
|
||||
import cn.hutool.core.map.MapUtil;
|
||||
import cn.iocoder.yudao.framework.tenant.core.util.TenantUtils;
|
||||
import cn.iocoder.yudao.module.bpm.framework.flowable.core.mq.message.task.TodoTaskReminderMessage;
|
||||
import cn.iocoder.yudao.module.system.api.notify.NotifyMessageSendApi;
|
||||
import cn.iocoder.yudao.module.system.api.notify.dto.NotifySendSingleToUserReqDTO;
|
||||
import jakarta.annotation.Resource;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.context.event.EventListener;
|
||||
import org.springframework.scheduling.annotation.Async;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* 待办任务提醒 - 站内信的消费者
|
||||
*
|
||||
* @author jason
|
||||
*/
|
||||
@Component
|
||||
@Slf4j
|
||||
public class SysNotifyTodoTaskReminderConsumer {
|
||||
|
||||
private static final String TASK_REMIND_TEMPLATE_CODE = "user_task_remind";
|
||||
|
||||
@Resource
|
||||
private NotifyMessageSendApi notifyMessageSendApi;
|
||||
|
||||
@EventListener
|
||||
@Async
|
||||
public void onMessage(TodoTaskReminderMessage message) {
|
||||
log.info("站内信消费者接收到消息 [消息内容({})] ", message);
|
||||
TenantUtils.execute(message.getTenantId(), ()-> {
|
||||
Map<String,Object> templateParams = MapUtil.newHashMap();
|
||||
templateParams.put("name", message.getTaskName());
|
||||
NotifySendSingleToUserReqDTO req = new NotifySendSingleToUserReqDTO().setUserId(message.getUserId())
|
||||
.setTemplateCode(TASK_REMIND_TEMPLATE_CODE).setTemplateParams(templateParams);
|
||||
notifyMessageSendApi.sendSingleMessageToAdmin(req);
|
||||
});
|
||||
}
|
||||
}
|
@ -1,34 +0,0 @@
|
||||
package cn.iocoder.yudao.module.bpm.framework.flowable.core.mq.message.task;
|
||||
|
||||
import jakarta.validation.constraints.NotEmpty;
|
||||
import jakarta.validation.constraints.NotNull;
|
||||
import lombok.Data;
|
||||
|
||||
/**
|
||||
* 待办任务提醒消息
|
||||
*
|
||||
* @author jason
|
||||
*/
|
||||
@Data
|
||||
public class TodoTaskReminderMessage {
|
||||
|
||||
/**
|
||||
* 租户 Id
|
||||
*/
|
||||
@NotNull(message = "租户 Id 不能未空")
|
||||
private Long tenantId;
|
||||
|
||||
/**
|
||||
* 用户Id
|
||||
*/
|
||||
@NotNull(message = "用户 Id 不能未空")
|
||||
private Long userId;
|
||||
|
||||
/**
|
||||
* 任务名称
|
||||
*/
|
||||
@NotEmpty(message = "任务名称不能未空")
|
||||
private String taskName;
|
||||
|
||||
// TODO 暂时只有站内信通知. 后面可以增加
|
||||
}
|
@ -1,27 +0,0 @@
|
||||
package cn.iocoder.yudao.module.bpm.framework.flowable.core.mq.producer.task;
|
||||
|
||||
import cn.iocoder.yudao.module.bpm.framework.flowable.core.mq.message.task.TodoTaskReminderMessage;
|
||||
import jakarta.annotation.Resource;
|
||||
import jakarta.validation.Valid;
|
||||
import org.springframework.context.ApplicationContext;
|
||||
import org.springframework.stereotype.Component;
|
||||
import org.springframework.validation.annotation.Validated;
|
||||
|
||||
// TODO @jason:建议直接调用 BpmMessageService 哈;更简化一点~
|
||||
/**
|
||||
* 待办任务提醒 Producer
|
||||
*
|
||||
* @author jason
|
||||
*/
|
||||
@Component
|
||||
@Validated
|
||||
public class TodoTaskReminderProducer {
|
||||
|
||||
@Resource
|
||||
private ApplicationContext applicationContext;
|
||||
|
||||
public void sendReminderMessage(@Valid TodoTaskReminderMessage message) {
|
||||
applicationContext.publishEvent(message);
|
||||
}
|
||||
|
||||
}
|
@ -53,6 +53,10 @@ public class BpmnModelUtils {
|
||||
return BpmnModelUtils.parseExtensionElement(flowElement, USER_TASK_REJECT_RETURN_TASK_ID);
|
||||
}
|
||||
|
||||
public static Integer parseAssignStartUserHandlerType(FlowElement userTask) {
|
||||
return NumberUtils.parseInt(BpmnModelUtils.parseExtensionElement(userTask, USER_TASK_ASSIGN_START_USER_HANDLER_TYPE));
|
||||
}
|
||||
|
||||
public static String parseExtensionElement(FlowElement flowElement, String elementName) {
|
||||
if (flowElement == null) {
|
||||
return null;
|
||||
|
@ -1,6 +1,8 @@
|
||||
package cn.iocoder.yudao.module.bpm.framework.flowable.core.util;
|
||||
|
||||
import cn.hutool.core.util.ObjectUtil;
|
||||
import cn.iocoder.yudao.framework.tenant.core.context.TenantContextHolder;
|
||||
import cn.iocoder.yudao.framework.tenant.core.util.TenantUtils;
|
||||
import cn.iocoder.yudao.module.bpm.framework.flowable.core.enums.BpmConstants;
|
||||
import org.flowable.common.engine.api.delegate.Expression;
|
||||
import org.flowable.common.engine.api.variable.VariableContainer;
|
||||
@ -16,6 +18,7 @@ import org.flowable.task.api.TaskInfo;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Objects;
|
||||
|
||||
/**
|
||||
* Flowable 相关的工具方法
|
||||
@ -39,6 +42,16 @@ public class FlowableUtils {
|
||||
return tenantId != null ? String.valueOf(tenantId) : ProcessEngineConfiguration.NO_TENANT_ID;
|
||||
}
|
||||
|
||||
public static void execute(String tenantIdStr, Runnable runnable) {
|
||||
if (ObjectUtil.isEmpty(tenantIdStr)
|
||||
|| Objects.equals(tenantIdStr, ProcessEngineConfiguration.NO_TENANT_ID)) {
|
||||
runnable.run();
|
||||
} else {
|
||||
Long tenantId = Long.valueOf(tenantIdStr);
|
||||
TenantUtils.execute(tenantId, runnable);
|
||||
}
|
||||
}
|
||||
|
||||
// ========== Execution 相关的工具方法 ==========
|
||||
|
||||
/**
|
||||
|
@ -10,6 +10,7 @@ import cn.iocoder.yudao.framework.common.util.collection.CollectionUtils;
|
||||
import cn.iocoder.yudao.module.bpm.controller.admin.definition.vo.model.simple.BpmSimpleModelNodeVO;
|
||||
import cn.iocoder.yudao.module.bpm.controller.admin.definition.vo.model.simple.BpmSimpleModelNodeVO.RejectHandler;
|
||||
import cn.iocoder.yudao.module.bpm.enums.definition.BpmApproveMethodEnum;
|
||||
import cn.iocoder.yudao.module.bpm.enums.definition.BpmBoundaryEventType;
|
||||
import cn.iocoder.yudao.module.bpm.enums.definition.BpmSimpleModeConditionType;
|
||||
import cn.iocoder.yudao.module.bpm.enums.definition.BpmSimpleModelNodeType;
|
||||
import cn.iocoder.yudao.module.bpm.framework.flowable.core.enums.BpmnModelConstants;
|
||||
@ -25,7 +26,6 @@ import java.util.Objects;
|
||||
|
||||
import static cn.iocoder.yudao.module.bpm.controller.admin.definition.vo.model.simple.BpmSimpleModelNodeVO.OperationButtonSetting;
|
||||
import static cn.iocoder.yudao.module.bpm.controller.admin.definition.vo.model.simple.BpmSimpleModelNodeVO.TimeoutHandler;
|
||||
import static cn.iocoder.yudao.module.bpm.enums.definition.BpmBoundaryEventType.USER_TASK_TIMEOUT;
|
||||
import static cn.iocoder.yudao.module.bpm.enums.definition.BpmSimpleModelNodeType.*;
|
||||
import static cn.iocoder.yudao.module.bpm.enums.definition.BpmUserTaskTimeoutHandlerType.REMINDER;
|
||||
import static cn.iocoder.yudao.module.bpm.framework.flowable.core.enums.BpmnModelConstants.*;
|
||||
@ -59,7 +59,6 @@ public class SimpleModelUtils {
|
||||
*/
|
||||
public static final String APPROVE_BY_RATIO_COMPLETE_EXPRESSION = "${ nrOfCompletedInstances/nrOfInstances >= %s}";
|
||||
|
||||
// TODO-DONE @jason:建议方法名,改成 buildBpmnModel
|
||||
// TODO @yunai:注释需要完善下;
|
||||
|
||||
/**
|
||||
@ -339,32 +338,41 @@ public class SimpleModelUtils {
|
||||
List<FlowElement> flowElements = new ArrayList<>();
|
||||
UserTask userTask = buildBpmnUserTask(node);
|
||||
flowElements.add(userTask);
|
||||
|
||||
// 添加用户任务的 Timer Boundary Event, 用于任务的审批超时处理
|
||||
if (node.getTimeoutHandler() != null && node.getTimeoutHandler().getEnable()) {
|
||||
// 添加用户任务的 Timer Boundary Event, 用于任务的超时处理
|
||||
BoundaryEvent boundaryEvent = buildUserTaskTimerBoundaryEvent(userTask, node.getTimeoutHandler());
|
||||
flowElements.add(boundaryEvent);
|
||||
}
|
||||
return flowElements;
|
||||
}
|
||||
|
||||
/**
|
||||
* 添加 UserTask 用户的审批超时 BoundaryEvent 事件
|
||||
*
|
||||
* @param userTask 审批任务
|
||||
* @param timeoutHandler 超时处理器
|
||||
* @return BoundaryEvent 超时事件
|
||||
*/
|
||||
private static BoundaryEvent buildUserTaskTimerBoundaryEvent(UserTask userTask, TimeoutHandler timeoutHandler) {
|
||||
// 定时器边界事件
|
||||
// 1.1 定时器边界事件
|
||||
BoundaryEvent boundaryEvent = new BoundaryEvent();
|
||||
boundaryEvent.setId("Event-" + IdUtil.fastUUID());
|
||||
// 设置关联的任务为不会被中断
|
||||
boundaryEvent.setCancelActivity(false);
|
||||
boundaryEvent.setCancelActivity(false); // 设置关联的任务为不会被中断
|
||||
boundaryEvent.setAttachedToRef(userTask);
|
||||
// 1.2 定义超时时间、最大提醒次数
|
||||
TimerEventDefinition eventDefinition = new TimerEventDefinition();
|
||||
eventDefinition.setTimeDuration(timeoutHandler.getTimeDuration());
|
||||
if (Objects.equals(REMINDER.getAction(), timeoutHandler.getAction()) &&
|
||||
timeoutHandler.getMaxRemindCount() != null && timeoutHandler.getMaxRemindCount() > 1) {
|
||||
// 最大提醒次数
|
||||
eventDefinition.setTimeCycle(String.format("R%d/%s", timeoutHandler.getMaxRemindCount(), timeoutHandler.getTimeDuration()));
|
||||
eventDefinition.setTimeCycle(String.format("R%d/%s",
|
||||
timeoutHandler.getMaxRemindCount(), timeoutHandler.getTimeDuration()));
|
||||
}
|
||||
boundaryEvent.addEventDefinition(eventDefinition);
|
||||
// 添加定时器边界事件类型
|
||||
addExtensionElement(boundaryEvent, BOUNDARY_EVENT_TYPE, USER_TASK_TIMEOUT.getType().toString());
|
||||
// 添加超时执行动作元素
|
||||
|
||||
// 2.1 添加定时器边界事件类型
|
||||
addExtensionElement(boundaryEvent, BOUNDARY_EVENT_TYPE, BpmBoundaryEventType.USER_TASK_TIMEOUT.getType().toString());
|
||||
// 2.2 添加超时执行动作元素
|
||||
addExtensionElement(boundaryEvent, USER_TASK_TIMEOUT_HANDLER_ACTION, StrUtil.toStringOrNull(timeoutHandler.getAction()));
|
||||
return boundaryEvent;
|
||||
}
|
||||
@ -448,8 +456,6 @@ public class SimpleModelUtils {
|
||||
userTask.setDueDate(node.getTimeoutHandler().getTimeDuration());
|
||||
}
|
||||
|
||||
// TODO 芋艿 + jason:要不要基于服务任务,实现或签下的审批不通过?或者说,按比例审批
|
||||
|
||||
// TODO @jason:addCandidateElements、processMultiInstanceLoopCharacteristics 建议一起搞哈?
|
||||
// 添加候选人元素
|
||||
addCandidateElements(node.getCandidateStrategy(), node.getCandidateParam(), userTask);
|
||||
@ -461,10 +467,11 @@ public class SimpleModelUtils {
|
||||
processMultiInstanceLoopCharacteristics(node.getApproveMethod(), node.getApproveRatio(), userTask);
|
||||
// 添加任务被拒绝的处理元素
|
||||
addTaskRejectElements(node.getRejectHandler(), userTask);
|
||||
// 添加用户任务的审批人与发起人相同时的处理元素
|
||||
addAssignStartUserHandlerType(node.getAssignStartUserHandlerType(), userTask);
|
||||
return userTask;
|
||||
}
|
||||
|
||||
|
||||
private static void addTaskRejectElements(RejectHandler rejectHandler, UserTask userTask) {
|
||||
if (rejectHandler == null) {
|
||||
return;
|
||||
@ -473,6 +480,13 @@ public class SimpleModelUtils {
|
||||
addExtensionElement(userTask, USER_TASK_REJECT_RETURN_TASK_ID, rejectHandler.getReturnNodeId());
|
||||
}
|
||||
|
||||
private static void addAssignStartUserHandlerType(Integer assignStartUserHandlerType, UserTask userTask) {
|
||||
if (assignStartUserHandlerType == null) {
|
||||
return;
|
||||
}
|
||||
addExtensionElement(userTask, USER_TASK_ASSIGN_START_USER_HANDLER_TYPE, assignStartUserHandlerType.toString());
|
||||
}
|
||||
|
||||
private static void processMultiInstanceLoopCharacteristics(Integer approveMethod, Integer approveRatio, UserTask userTask) {
|
||||
BpmApproveMethodEnum bpmApproveMethodEnum = BpmApproveMethodEnum.valueOf(approveMethod);
|
||||
if (bpmApproveMethodEnum == null || bpmApproveMethodEnum == BpmApproveMethodEnum.RANDOM) {
|
||||
|
@ -3,7 +3,7 @@ package cn.iocoder.yudao.module.bpm.service.message;
|
||||
import cn.iocoder.yudao.module.bpm.service.message.dto.BpmMessageSendWhenProcessInstanceApproveReqDTO;
|
||||
import cn.iocoder.yudao.module.bpm.service.message.dto.BpmMessageSendWhenProcessInstanceRejectReqDTO;
|
||||
import cn.iocoder.yudao.module.bpm.service.message.dto.BpmMessageSendWhenTaskCreatedReqDTO;
|
||||
|
||||
import cn.iocoder.yudao.module.bpm.service.message.dto.BpmMessageSendWhenTaskTimeoutReqDTO;
|
||||
import jakarta.validation.Valid;
|
||||
|
||||
/**
|
||||
@ -36,4 +36,11 @@ public interface BpmMessageService {
|
||||
*/
|
||||
void sendMessageWhenTaskAssigned(@Valid BpmMessageSendWhenTaskCreatedReqDTO reqDTO);
|
||||
|
||||
/**
|
||||
* 发送任务审批超时的消息
|
||||
*
|
||||
* @param reqDTO 发送信息
|
||||
*/
|
||||
void sendMessageWhenTaskTimeout(@Valid BpmMessageSendWhenTaskTimeoutReqDTO reqDTO);
|
||||
|
||||
}
|
||||
|
@ -6,6 +6,7 @@ import cn.iocoder.yudao.module.bpm.enums.message.BpmMessageEnum;
|
||||
import cn.iocoder.yudao.module.bpm.service.message.dto.BpmMessageSendWhenProcessInstanceApproveReqDTO;
|
||||
import cn.iocoder.yudao.module.bpm.service.message.dto.BpmMessageSendWhenProcessInstanceRejectReqDTO;
|
||||
import cn.iocoder.yudao.module.bpm.service.message.dto.BpmMessageSendWhenTaskCreatedReqDTO;
|
||||
import cn.iocoder.yudao.module.bpm.service.message.dto.BpmMessageSendWhenTaskTimeoutReqDTO;
|
||||
import cn.iocoder.yudao.module.system.api.sms.SmsSendApi;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.stereotype.Service;
|
||||
@ -61,6 +62,16 @@ public class BpmMessageServiceImpl implements BpmMessageService {
|
||||
BpmMessageEnum.TASK_ASSIGNED.getSmsTemplateCode(), templateParams));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void sendMessageWhenTaskTimeout(BpmMessageSendWhenTaskTimeoutReqDTO reqDTO) {
|
||||
Map<String, Object> templateParams = new HashMap<>();
|
||||
templateParams.put("processInstanceName", reqDTO.getProcessInstanceName());
|
||||
templateParams.put("taskName", reqDTO.getTaskName());
|
||||
templateParams.put("detailUrl", getProcessInstanceDetailUrl(reqDTO.getProcessInstanceId()));
|
||||
smsSendApi.sendSingleSmsToAdmin(BpmMessageConvert.INSTANCE.convert(reqDTO.getAssigneeUserId(),
|
||||
BpmMessageEnum.TASK_TIMEOUT.getSmsTemplateCode(), templateParams));
|
||||
}
|
||||
|
||||
private String getProcessInstanceDetailUrl(String taskId) {
|
||||
return webProperties.getAdminUi().getUrl() + "/bpm/process-instance/detail?id=" + taskId;
|
||||
}
|
||||
|
@ -0,0 +1,41 @@
|
||||
package cn.iocoder.yudao.module.bpm.service.message.dto;
|
||||
|
||||
import jakarta.validation.constraints.NotEmpty;
|
||||
import jakarta.validation.constraints.NotNull;
|
||||
import lombok.Data;
|
||||
|
||||
/**
|
||||
* BPM 发送任务审批超时 Request DTO
|
||||
*/
|
||||
@Data
|
||||
public class BpmMessageSendWhenTaskTimeoutReqDTO {
|
||||
|
||||
/**
|
||||
* 流程实例的编号
|
||||
*/
|
||||
@NotEmpty(message = "流程实例的编号不能为空")
|
||||
private String processInstanceId;
|
||||
/**
|
||||
* 流程实例的名字
|
||||
*/
|
||||
@NotEmpty(message = "流程实例的名字不能为空")
|
||||
private String processInstanceName;
|
||||
|
||||
/**
|
||||
* 流程任务的编号
|
||||
*/
|
||||
@NotEmpty(message = "流程任务的编号不能为空")
|
||||
private String taskId;
|
||||
/**
|
||||
* 流程任务的名字
|
||||
*/
|
||||
@NotEmpty(message = "流程任务的名字不能为空")
|
||||
private String taskName;
|
||||
|
||||
/**
|
||||
* 审批人的用户编号
|
||||
*/
|
||||
@NotNull(message = "审批人的用户编号不能为空")
|
||||
private Long assigneeUserId;
|
||||
|
||||
}
|
File diff suppressed because one or more lines are too long
@ -207,4 +207,13 @@ public interface BpmTaskService {
|
||||
*/
|
||||
void processTaskAssigned(Task task);
|
||||
|
||||
/**
|
||||
* 处理 Task 审批超时事件,可能会处理多个当前审批中的任务
|
||||
*
|
||||
* @param processInstanceId 流程示例编号
|
||||
* @param taskDefineKey 任务 Key
|
||||
* @param taskAction 处理类型
|
||||
*/
|
||||
void processTaskTimeout(String processInstanceId, String taskDefineKey, Integer taskAction);
|
||||
|
||||
}
|
||||
|
@ -1,15 +1,20 @@
|
||||
package cn.iocoder.yudao.module.bpm.service.task;
|
||||
|
||||
import cn.hutool.core.collection.CollUtil;
|
||||
import cn.hutool.core.lang.Assert;
|
||||
import cn.hutool.core.util.*;
|
||||
import cn.hutool.extra.spring.SpringUtil;
|
||||
import cn.iocoder.yudao.framework.common.pojo.PageResult;
|
||||
import cn.iocoder.yudao.framework.common.util.date.DateUtils;
|
||||
import cn.iocoder.yudao.framework.common.util.number.NumberUtils;
|
||||
import cn.iocoder.yudao.framework.common.util.object.ObjectUtils;
|
||||
import cn.iocoder.yudao.framework.common.util.object.PageUtils;
|
||||
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.convert.task.BpmTaskConvert;
|
||||
import cn.iocoder.yudao.module.bpm.enums.definition.BpmUserTaskAssignStartUserHandlerTypeEnum;
|
||||
import cn.iocoder.yudao.module.bpm.enums.definition.BpmUserTaskRejectHandlerType;
|
||||
import cn.iocoder.yudao.module.bpm.enums.definition.BpmUserTaskTimeoutHandlerType;
|
||||
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.BpmTaskSignTypeEnum;
|
||||
@ -19,6 +24,9 @@ import cn.iocoder.yudao.module.bpm.framework.flowable.core.util.BpmnModelUtils;
|
||||
import cn.iocoder.yudao.module.bpm.framework.flowable.core.util.FlowableUtils;
|
||||
import cn.iocoder.yudao.module.bpm.service.definition.BpmModelService;
|
||||
import cn.iocoder.yudao.module.bpm.service.message.BpmMessageService;
|
||||
import cn.iocoder.yudao.module.bpm.service.message.dto.BpmMessageSendWhenTaskTimeoutReqDTO;
|
||||
import cn.iocoder.yudao.module.system.api.dept.DeptApi;
|
||||
import cn.iocoder.yudao.module.system.api.dept.dto.DeptRespDTO;
|
||||
import cn.iocoder.yudao.module.system.api.user.AdminUserApi;
|
||||
import cn.iocoder.yudao.module.system.api.user.dto.AdminUserRespDTO;
|
||||
import jakarta.annotation.Resource;
|
||||
@ -44,7 +52,6 @@ import org.springframework.stereotype.Service;
|
||||
import org.springframework.transaction.annotation.Transactional;
|
||||
import org.springframework.transaction.support.TransactionSynchronization;
|
||||
import org.springframework.transaction.support.TransactionSynchronizationManager;
|
||||
import org.springframework.util.Assert;
|
||||
|
||||
import java.util.*;
|
||||
import java.util.stream.Stream;
|
||||
@ -77,12 +84,14 @@ public class BpmTaskServiceImpl implements BpmTaskService {
|
||||
@Resource
|
||||
private BpmProcessInstanceCopyService processInstanceCopyService;
|
||||
@Resource
|
||||
private BpmModelService bpmModelService;
|
||||
private BpmModelService modelService;
|
||||
@Resource
|
||||
private BpmMessageService messageService;
|
||||
|
||||
@Resource
|
||||
private AdminUserApi adminUserApi;
|
||||
@Resource
|
||||
private DeptApi deptApi;
|
||||
|
||||
// ========== Query 查询相关方法 ==========
|
||||
|
||||
@ -226,7 +235,7 @@ public class BpmTaskServiceImpl implements BpmTaskService {
|
||||
// 1.1 校验当前任务 task 存在
|
||||
Task task = validateTaskExist(id);
|
||||
// 1.2 根据流程定义获取流程模型信息
|
||||
BpmnModel bpmnModel = bpmModelService.getBpmnModelByDefinitionId(task.getProcessDefinitionId());
|
||||
BpmnModel bpmnModel = modelService.getBpmnModelByDefinitionId(task.getProcessDefinitionId());
|
||||
FlowElement source = BpmnModelUtils.getFlowElementById(bpmnModel, task.getTaskDefinitionKey());
|
||||
if (source == null) {
|
||||
throw exception(TASK_NOT_EXISTS);
|
||||
@ -496,12 +505,12 @@ public class BpmTaskServiceImpl implements BpmTaskService {
|
||||
}
|
||||
|
||||
// 3. 根据不同的 RejectHandler 处理策略
|
||||
BpmnModel bpmnModel = bpmModelService.getBpmnModelByDefinitionId(task.getProcessDefinitionId());
|
||||
FlowElement flowElement = BpmnModelUtils.getFlowElementById(bpmnModel, task.getTaskDefinitionKey());
|
||||
BpmnModel bpmnModel = modelService.getBpmnModelByDefinitionId(task.getProcessDefinitionId());
|
||||
FlowElement userTaskElement = BpmnModelUtils.getFlowElementById(bpmnModel, task.getTaskDefinitionKey());
|
||||
// 3.1 情况一:驳回到指定的任务节点
|
||||
BpmUserTaskRejectHandlerType userTaskRejectHandlerType = BpmnModelUtils.parseRejectHandlerType(flowElement);
|
||||
BpmUserTaskRejectHandlerType userTaskRejectHandlerType = BpmnModelUtils.parseRejectHandlerType(userTaskElement);
|
||||
if (userTaskRejectHandlerType == BpmUserTaskRejectHandlerType.RETURN_USER_TASK) {
|
||||
String returnTaskId = BpmnModelUtils.parseReturnTaskId(flowElement);
|
||||
String returnTaskId = BpmnModelUtils.parseReturnTaskId(userTaskElement);
|
||||
Assert.notNull(returnTaskId, "回退的节点不能为空");
|
||||
returnTask(userId, new BpmTaskReturnReqVO().setId(task.getId())
|
||||
.setTargetTaskDefinitionKey(returnTaskId).setReason(reqVO.getReason()));
|
||||
@ -560,7 +569,7 @@ public class BpmTaskServiceImpl implements BpmTaskService {
|
||||
*/
|
||||
private FlowElement validateTargetTaskCanReturn(String sourceKey, String targetKey, String processDefinitionId) {
|
||||
// 1.1 获取流程模型信息
|
||||
BpmnModel bpmnModel = bpmModelService.getBpmnModelByDefinitionId(processDefinitionId);
|
||||
BpmnModel bpmnModel = modelService.getBpmnModelByDefinitionId(processDefinitionId);
|
||||
// 1.3 获取当前任务节点元素
|
||||
FlowElement source = BpmnModelUtils.getFlowElementById(bpmnModel, sourceKey);
|
||||
// 1.3 获取跳转的节点元素
|
||||
@ -688,7 +697,7 @@ public class BpmTaskServiceImpl implements BpmTaskService {
|
||||
});
|
||||
|
||||
// 2. 终止流程
|
||||
BpmnModel bpmnModel = bpmModelService.getBpmnModelByDefinitionId(taskList.get(0).getProcessDefinitionId());
|
||||
BpmnModel bpmnModel = modelService.getBpmnModelByDefinitionId(taskList.get(0).getProcessDefinitionId());
|
||||
List<String> activityIds = CollUtil.newArrayList(convertSet(taskList, Task::getTaskDefinitionKey));
|
||||
EndEvent endEvent = BpmnModelUtils.getEndEvent(bpmnModel);
|
||||
Assert.notNull(endEvent, "结束节点不能未空");
|
||||
@ -913,16 +922,110 @@ public class BpmTaskServiceImpl implements BpmTaskService {
|
||||
@Override
|
||||
public void afterCommit() {
|
||||
if (StrUtil.isEmpty(task.getAssignee())) {
|
||||
log.error("[processTaskAssigned][taskId({}) 没有分配到负责人]", task.getId());
|
||||
return;
|
||||
}
|
||||
ProcessInstance processInstance = processInstanceService.getProcessInstance(task.getProcessInstanceId());
|
||||
if (processInstance != null) {
|
||||
AdminUserRespDTO startUser = adminUserApi.getUser(Long.valueOf(processInstance.getStartUserId()));
|
||||
messageService.sendMessageWhenTaskAssigned(BpmTaskConvert.INSTANCE.convert(processInstance, startUser, task));
|
||||
if (processInstance == null) {
|
||||
log.error("[processTaskAssigned][taskId({}) 没有找到流程实例]", task.getId());
|
||||
return;
|
||||
}
|
||||
|
||||
// 审批人与提交人为同一人时,根据策略进行处理
|
||||
if (StrUtil.equals(task.getAssignee(), processInstance.getStartUserId())) {
|
||||
BpmnModel bpmnModel = modelService.getBpmnModelByDefinitionId(processInstance.getProcessDefinitionId());
|
||||
if (bpmnModel == null) {
|
||||
log.error("[processTaskAssigned][taskId({}) 没有找到流程模型]", task.getId());
|
||||
return;
|
||||
}
|
||||
FlowElement userTaskElement = BpmnModelUtils.getFlowElementById(bpmnModel, task.getTaskDefinitionKey());
|
||||
Integer assignStartUserHandlerType = BpmnModelUtils.parseAssignStartUserHandlerType(userTaskElement);
|
||||
|
||||
// 情况一:自动跳过
|
||||
if (ObjectUtils.equalsAny(assignStartUserHandlerType,
|
||||
BpmUserTaskAssignStartUserHandlerTypeEnum.SKIP.getType())) {
|
||||
getSelf().approveTask(Long.valueOf(task.getAssignee()), new BpmTaskApproveReqVO()
|
||||
.setId(task.getId()).setReason("审批人与提交人为同一人时,自动通过"));
|
||||
return;
|
||||
}
|
||||
// 情况二:转交给部门负责人审批
|
||||
if (ObjectUtils.equalsAny(assignStartUserHandlerType,
|
||||
BpmUserTaskAssignStartUserHandlerTypeEnum.ASSIGN_DEPT_LEADER.getType())) {
|
||||
AdminUserRespDTO startUser = adminUserApi.getUser(Long.valueOf(processInstance.getStartUserId()));
|
||||
Assert.notNull(startUser, "提交人({})信息为空", processInstance.getStartUserId());
|
||||
DeptRespDTO dept = startUser.getDeptId() != null ? deptApi.getDept(startUser.getDeptId()) : null;
|
||||
Assert.notNull(dept, "提交人({})部门({})信息为空", processInstance.getStartUserId(), startUser.getDeptId());
|
||||
// 找不到部门负责人的情况下,自动审批通过
|
||||
// noinspection DataFlowIssue
|
||||
if (dept.getLeaderUserId() == null) {
|
||||
getSelf().approveTask(Long.valueOf(task.getAssignee()), new BpmTaskApproveReqVO()
|
||||
.setId(task.getId()).setReason("审批人与提交人为同一人时,找不到部门负责人,自动通过"));
|
||||
return;
|
||||
}
|
||||
// 找得到部门负责人的情况下,修改负责人
|
||||
if (ObjectUtil.notEqual(dept.getLeaderUserId(), startUser.getId())) {
|
||||
getSelf().transferTask(Long.valueOf(task.getAssignee()), new BpmTaskTransferReqVO()
|
||||
.setId(task.getId()).setAssigneeUserId(dept.getLeaderUserId())
|
||||
.setReason("审批人与提交人为同一人时,转交给部门负责人审批"));
|
||||
return;
|
||||
}
|
||||
// 如果部门负责人是自己,还是自己审批吧~
|
||||
}
|
||||
}
|
||||
|
||||
AdminUserRespDTO startUser = adminUserApi.getUser(Long.valueOf(processInstance.getStartUserId()));
|
||||
messageService.sendMessageWhenTaskAssigned(BpmTaskConvert.INSTANCE.convert(processInstance, startUser, task));
|
||||
}
|
||||
|
||||
});
|
||||
}
|
||||
|
||||
@Override
|
||||
@Transactional(rollbackFor = Exception.class)
|
||||
public void processTaskTimeout(String processInstanceId, String taskDefineKey, Integer taskAction) {
|
||||
ProcessInstance processInstance = processInstanceService.getProcessInstance(processInstanceId);
|
||||
if (processInstance == null) {
|
||||
log.error("[processTaskTimeout][processInstanceId({}) 没有找到流程实例]", processInstanceId);
|
||||
return;
|
||||
}
|
||||
List<Task> taskList = getRunningTaskListByProcessInstanceId(processInstanceId, true, taskDefineKey);
|
||||
// TODO 优化:未来需要考虑加签的情况
|
||||
if (CollUtil.isEmpty(taskList)) {
|
||||
log.error("[processTaskTimeout][processInstanceId({}) 定义Key({}) 没有找到任务]", processInstanceId, taskDefineKey);
|
||||
return;
|
||||
}
|
||||
|
||||
taskList.forEach(task -> FlowableUtils.execute(task.getTenantId(), () -> {
|
||||
// 情况一:自动提醒
|
||||
if (Objects.equals(taskAction, BpmUserTaskTimeoutHandlerType.REMINDER.getAction())) {
|
||||
messageService.sendMessageWhenTaskTimeout(new BpmMessageSendWhenTaskTimeoutReqDTO()
|
||||
.setProcessInstanceId(processInstanceId).setProcessInstanceName(processInstance.getName())
|
||||
.setTaskId(task.getId()).setTaskName(task.getName()).setAssigneeUserId(Long.parseLong(task.getAssignee())));
|
||||
return;
|
||||
}
|
||||
|
||||
// 情况二:自动同意
|
||||
if (Objects.equals(taskAction, BpmUserTaskTimeoutHandlerType.APPROVE.getAction())) {
|
||||
approveTask(Long.parseLong(task.getAssignee()),
|
||||
new BpmTaskApproveReqVO().setId(task.getId()).setReason("超时系统自动同意"));
|
||||
return;
|
||||
}
|
||||
|
||||
// 情况三:自动拒绝
|
||||
if (Objects.equals(taskAction, BpmUserTaskTimeoutHandlerType.REJECT.getAction())) {
|
||||
rejectTask(Long.parseLong(task.getAssignee()),
|
||||
new BpmTaskRejectReqVO().setId(task.getId()).setReason("超时系统自动拒绝"));
|
||||
}
|
||||
}));
|
||||
}
|
||||
|
||||
/**
|
||||
* 获得自身的代理对象,解决 AOP 生效问题
|
||||
*
|
||||
* @return 自己
|
||||
*/
|
||||
private BpmTaskServiceImpl getSelf() {
|
||||
return SpringUtil.getBean(getClass());
|
||||
}
|
||||
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user