mirror of
				https://gitee.com/hhyykk/ipms-sjy.git
				synced 2025-10-30 01:38:43 +08:00 
			
		
		
		
	仿钉钉流程设计- 审批节点超时处理
This commit is contained in:
		| @@ -0,0 +1,24 @@ | |||||||
|  | package cn.iocoder.yudao.module.bpm.enums.definition; | ||||||
|  |  | ||||||
|  | import cn.hutool.core.util.ArrayUtil; | ||||||
|  | import lombok.AllArgsConstructor; | ||||||
|  | import lombok.Getter; | ||||||
|  |  | ||||||
|  | /** | ||||||
|  |  * 定时器边界事件类型枚举 | ||||||
|  |  * | ||||||
|  |  * @author jason | ||||||
|  |  */ | ||||||
|  | @Getter | ||||||
|  | @AllArgsConstructor | ||||||
|  | public enum BpmTimerBoundaryEventType { | ||||||
|  |  | ||||||
|  |     USER_TASK_TIMEOUT(1,"用户任务超时"); | ||||||
|  |  | ||||||
|  |     private final Integer type; | ||||||
|  |     private final String name; | ||||||
|  |  | ||||||
|  |     public static BpmTimerBoundaryEventType typeOf(Integer type) { | ||||||
|  |         return ArrayUtil.firstMatch(eventType -> eventType.getType().equals(type), values()); | ||||||
|  |     } | ||||||
|  | } | ||||||
| @@ -0,0 +1,26 @@ | |||||||
|  | package cn.iocoder.yudao.module.bpm.enums.definition; | ||||||
|  |  | ||||||
|  | import cn.hutool.core.util.ArrayUtil; | ||||||
|  | import lombok.AllArgsConstructor; | ||||||
|  | import lombok.Getter; | ||||||
|  |  | ||||||
|  | /** | ||||||
|  |  * 用户任务超时处理执行动作枚举 | ||||||
|  |  * | ||||||
|  |  * @author jason | ||||||
|  |  */ | ||||||
|  | @Getter | ||||||
|  | @AllArgsConstructor | ||||||
|  | public enum BpmUserTaskTimeoutActionEnum { | ||||||
|  |  | ||||||
|  |     AUTO_REMINDER(1,"自动提醒"), | ||||||
|  |     AUTO_APPROVE(2, "自动同意"), | ||||||
|  |     AUTO_REJECT(3, "自动拒绝"); | ||||||
|  |  | ||||||
|  |     private final Integer action; | ||||||
|  |     private final String name; | ||||||
|  |  | ||||||
|  |     public static BpmUserTaskTimeoutActionEnum actionOf(Integer action) { | ||||||
|  |         return ArrayUtil.firstMatch(item -> item.getAction().equals(action), values()); | ||||||
|  |     } | ||||||
|  | } | ||||||
| @@ -30,6 +30,16 @@ public interface BpmnModelConstants { | |||||||
|      */ |      */ | ||||||
|     String USER_TASK_CANDIDATE_PARAM = "candidateParam"; |     String USER_TASK_CANDIDATE_PARAM = "candidateParam"; | ||||||
|  |  | ||||||
|  |     /** | ||||||
|  |      * BPMN ExtensionElement 的扩展属性,用于标记用户任务超时执行动作 | ||||||
|  |      */ | ||||||
|  |     String USER_TASK_TIMEOUT_HANDLER_ACTION = "timeoutAction"; | ||||||
|  |  | ||||||
|  |     /** | ||||||
|  |      * BPMN ExtensionElement 的扩展属性,用于标记定时边界事件类型 | ||||||
|  |      */ | ||||||
|  |     String TIMER_BOUNDARY_EVENT_TYPE = "timerBoundaryEventType"; | ||||||
|  |  | ||||||
|     /** |     /** | ||||||
|      * BPMN ExtensionElement 流程表单字段权限元素, 用于标记字段权限 |      * BPMN ExtensionElement 流程表单字段权限元素, 用于标记字段权限 | ||||||
|      */ |      */ | ||||||
|   | |||||||
| @@ -0,0 +1,117 @@ | |||||||
|  | package cn.iocoder.yudao.module.bpm.framework.flowable.core.listener; | ||||||
|  |  | ||||||
|  | import cn.hutool.core.collection.CollUtil; | ||||||
|  | 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.BpmTimerBoundaryEventType; | ||||||
|  | import cn.iocoder.yudao.module.bpm.enums.definition.BpmUserTaskTimeoutActionEnum; | ||||||
|  | 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.ExtensionElement; | ||||||
|  | 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.Optional; | ||||||
|  | import java.util.Set; | ||||||
|  |  | ||||||
|  | /** | ||||||
|  |  * 监听定时器触发事件 | ||||||
|  |  * | ||||||
|  |  * @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; | ||||||
|  |             ExtensionElement extensionElement = CollUtil.getFirst(boundaryEvent.getExtensionElements().get(BpmnModelConstants.TIMER_BOUNDARY_EVENT_TYPE)); | ||||||
|  |             Integer timerBoundaryEventType = NumberUtils.parseInt(Optional.ofNullable(extensionElement).map(ExtensionElement::getElementText).orElse(null)); | ||||||
|  |             BpmTimerBoundaryEventType bpmTimerBoundaryEventType = BpmTimerBoundaryEventType.typeOf(timerBoundaryEventType); | ||||||
|  |             // 类型为用户任务超时未处理的情况 | ||||||
|  |             if (bpmTimerBoundaryEventType == BpmTimerBoundaryEventType.USER_TASK_TIMEOUT) { | ||||||
|  |                 ExtensionElement timeoutActionElement = CollUtil.getFirst(boundaryEvent.getExtensionElements().get(BpmnModelConstants.USER_TASK_TIMEOUT_HANDLER_ACTION)); | ||||||
|  |                 Integer timeoutAction = NumberUtils.parseInt(Optional.ofNullable(timeoutActionElement).map(ExtensionElement::getElementText).orElse(null)); | ||||||
|  |                 processUserTaskTimeout(event.getProcessInstanceId(), boundaryEvent.getAttachedToRefId(), timeoutAction); | ||||||
|  |             } | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     private void processUserTaskTimeout(String processInstanceId, String taskDefKey, Integer timeoutAction) { | ||||||
|  |         BpmUserTaskTimeoutActionEnum userTaskTimeoutAction = BpmUserTaskTimeoutActionEnum.actionOf(timeoutAction); | ||||||
|  |         if (userTaskTimeoutAction != null) { | ||||||
|  |             // 查询超时未处理的任务 | ||||||
|  |             List<Task> taskList = bpmTaskService.getAssignedTaskListByConditions(processInstanceId, taskDefKey); | ||||||
|  |             taskList.forEach(task -> { | ||||||
|  |                 // 自动提醒 | ||||||
|  |                 if (userTaskTimeoutAction == BpmUserTaskTimeoutActionEnum.AUTO_REMINDER) { | ||||||
|  |                     TodoTaskReminderMessage message = new TodoTaskReminderMessage().setTenantId(Long.parseLong(task.getTenantId())) | ||||||
|  |                             .setUserId(Long.parseLong(task.getAssignee())).setTaskName(task.getName()); | ||||||
|  |                     todoTaskReminderProducer.sendReminderMessage(message); | ||||||
|  |                 } | ||||||
|  |                 // 自动同意 | ||||||
|  |                 if (userTaskTimeoutAction == BpmUserTaskTimeoutActionEnum.AUTO_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 == BpmUserTaskTimeoutActionEnum.AUTO_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); | ||||||
|  |                 } | ||||||
|  |             }); | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  | } | ||||||
| @@ -0,0 +1,42 @@ | |||||||
|  | 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); | ||||||
|  |         }); | ||||||
|  |     } | ||||||
|  | } | ||||||
| @@ -0,0 +1,34 @@ | |||||||
|  | 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 暂时只有站内信通知. 后面可以增加 | ||||||
|  | } | ||||||
| @@ -0,0 +1,25 @@ | |||||||
|  | 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; | ||||||
|  |  | ||||||
|  | /** | ||||||
|  |  * 待办任务提醒 Producer | ||||||
|  |  * | ||||||
|  |  * @author jason | ||||||
|  |  */ | ||||||
|  | @Component | ||||||
|  | @Validated | ||||||
|  | public class TodoTaskReminderProducer { | ||||||
|  |  | ||||||
|  |     @Resource | ||||||
|  |     private ApplicationContext applicationContext; | ||||||
|  |  | ||||||
|  |     public void sendReminderMessage(@Valid TodoTaskReminderMessage message) { | ||||||
|  |         applicationContext.publishEvent(message); | ||||||
|  |     } | ||||||
|  | } | ||||||
| @@ -0,0 +1,68 @@ | |||||||
|  | package cn.iocoder.yudao.module.bpm.framework.flowable.core.simple; | ||||||
|  |  | ||||||
|  | import lombok.Data; | ||||||
|  |  | ||||||
|  | import java.util.List; | ||||||
|  | import java.util.Map; | ||||||
|  |  | ||||||
|  | /** | ||||||
|  |  * 仿钉钉流程设计器审批节点配置 Model | ||||||
|  |  * | ||||||
|  |  * @author jason | ||||||
|  |  */ | ||||||
|  | @Data | ||||||
|  | public class SimpleModelUserTaskConfig { | ||||||
|  |  | ||||||
|  |     /** | ||||||
|  |      * 候选人策略 | ||||||
|  |      */ | ||||||
|  |     private  Integer candidateStrategy; | ||||||
|  |  | ||||||
|  |     /** | ||||||
|  |      * 候选人参数 | ||||||
|  |      */ | ||||||
|  |     private  String candidateParam; | ||||||
|  |  | ||||||
|  |     /** | ||||||
|  |      * 字段权限 | ||||||
|  |      */ | ||||||
|  |     private List<Map<String,String>> fieldsPermission; | ||||||
|  |  | ||||||
|  |     /** | ||||||
|  |      * 审批方式 | ||||||
|  |      */ | ||||||
|  |     private  Integer approveMethod; | ||||||
|  |  | ||||||
|  |  | ||||||
|  |     /** | ||||||
|  |      * 超时处理 | ||||||
|  |      */ | ||||||
|  |     private TimeoutHandler timeoutHandler; | ||||||
|  |  | ||||||
|  |  | ||||||
|  |     @Data | ||||||
|  |     public static class TimeoutHandler { | ||||||
|  |  | ||||||
|  |         /** | ||||||
|  |          * 是否开启超时处理 | ||||||
|  |          */ | ||||||
|  |         private Boolean enable; | ||||||
|  |  | ||||||
|  |         /** | ||||||
|  |          * 超时执行的动作 | ||||||
|  |          */ | ||||||
|  |         private Integer action; | ||||||
|  |  | ||||||
|  |         /** | ||||||
|  |          * 超时时间设置 | ||||||
|  |          */ | ||||||
|  |         private String timeDuration; | ||||||
|  |  | ||||||
|  |         /** | ||||||
|  |          * 如果执行动作是自动提醒, 最大提醒次数 | ||||||
|  |          */ | ||||||
|  |         private Integer maxRemindCount; | ||||||
|  |  | ||||||
|  |     } | ||||||
|  |  | ||||||
|  | } | ||||||
| @@ -5,27 +5,27 @@ import cn.hutool.core.collection.CollUtil; | |||||||
| import cn.hutool.core.lang.Assert; | import cn.hutool.core.lang.Assert; | ||||||
| import cn.hutool.core.lang.TypeReference; | import cn.hutool.core.lang.TypeReference; | ||||||
| import cn.hutool.core.map.MapUtil; | import cn.hutool.core.map.MapUtil; | ||||||
| import cn.hutool.core.util.ArrayUtil; | import cn.hutool.core.util.*; | ||||||
| import cn.hutool.core.util.BooleanUtil; |  | ||||||
| import cn.hutool.core.util.NumberUtil; |  | ||||||
| import cn.hutool.core.util.StrUtil; |  | ||||||
| import cn.iocoder.yudao.framework.common.util.collection.CollectionUtils; | import cn.iocoder.yudao.framework.common.util.collection.CollectionUtils; | ||||||
| import cn.iocoder.yudao.module.bpm.controller.admin.definition.vo.simple.BpmSimpleModelNodeVO; | import cn.iocoder.yudao.module.bpm.controller.admin.definition.vo.simple.BpmSimpleModelNodeVO; | ||||||
| import cn.iocoder.yudao.module.bpm.enums.definition.BpmApproveMethodEnum; | import cn.iocoder.yudao.module.bpm.enums.definition.BpmApproveMethodEnum; | ||||||
| import cn.iocoder.yudao.module.bpm.enums.definition.BpmSimpleModeConditionType; | import cn.iocoder.yudao.module.bpm.enums.definition.BpmSimpleModeConditionType; | ||||||
| import cn.iocoder.yudao.module.bpm.enums.definition.BpmSimpleModelNodeType; | import cn.iocoder.yudao.module.bpm.enums.definition.BpmSimpleModelNodeType; | ||||||
| import cn.iocoder.yudao.module.bpm.framework.flowable.core.enums.BpmnModelConstants; | import cn.iocoder.yudao.module.bpm.framework.flowable.core.enums.BpmnModelConstants; | ||||||
| import cn.iocoder.yudao.module.bpm.framework.flowable.core.enums.SimpleModelConstants; |  | ||||||
| import cn.iocoder.yudao.module.bpm.framework.flowable.core.simple.SimpleModelConditionGroups; | import cn.iocoder.yudao.module.bpm.framework.flowable.core.simple.SimpleModelConditionGroups; | ||||||
|  | import cn.iocoder.yudao.module.bpm.framework.flowable.core.simple.SimpleModelUserTaskConfig; | ||||||
| import org.flowable.bpmn.BpmnAutoLayout; | import org.flowable.bpmn.BpmnAutoLayout; | ||||||
| import org.flowable.bpmn.model.Process; | import org.flowable.bpmn.model.Process; | ||||||
| import org.flowable.bpmn.model.*; | import org.flowable.bpmn.model.*; | ||||||
|  |  | ||||||
| import java.util.List; | import java.util.List; | ||||||
| import java.util.Map; | import java.util.Map; | ||||||
|  | import java.util.Objects; | ||||||
|  |  | ||||||
| import static cn.iocoder.yudao.module.bpm.enums.definition.BpmSimpleModelNodeType.END_EVENT; | import static cn.iocoder.yudao.module.bpm.enums.definition.BpmSimpleModelNodeType.END_EVENT; | ||||||
| import static cn.iocoder.yudao.module.bpm.framework.flowable.core.enums.BpmnModelConstants.FORM_FIELD_PERMISSION_ELEMENT; | import static cn.iocoder.yudao.module.bpm.enums.definition.BpmTimerBoundaryEventType.USER_TASK_TIMEOUT; | ||||||
|  | import static cn.iocoder.yudao.module.bpm.enums.definition.BpmUserTaskTimeoutActionEnum.AUTO_REMINDER; | ||||||
|  | import static cn.iocoder.yudao.module.bpm.framework.flowable.core.enums.BpmnModelConstants.*; | ||||||
| import static cn.iocoder.yudao.module.bpm.framework.flowable.core.enums.SimpleModelConstants.*; | import static cn.iocoder.yudao.module.bpm.framework.flowable.core.enums.SimpleModelConstants.*; | ||||||
| import static org.flowable.bpmn.constants.BpmnXMLConstants.FLOWABLE_EXTENSIONS_NAMESPACE; | import static org.flowable.bpmn.constants.BpmnXMLConstants.FLOWABLE_EXTENSIONS_NAMESPACE; | ||||||
| import static org.flowable.bpmn.constants.BpmnXMLConstants.FLOWABLE_EXTENSIONS_PREFIX; | import static org.flowable.bpmn.constants.BpmnXMLConstants.FLOWABLE_EXTENSIONS_PREFIX; | ||||||
| @@ -205,8 +205,15 @@ public class SimpleModelUtils { | |||||||
|                 break; |                 break; | ||||||
|             } |             } | ||||||
|             case USER_TASK: { |             case USER_TASK: { | ||||||
|                 UserTask userTask = buildBpmnUserTask(simpleModelNode); |                 // 获取用户任务的配置 | ||||||
|  |                 SimpleModelUserTaskConfig userTaskConfig = BeanUtil.toBean(simpleModelNode.getAttributes(), SimpleModelUserTaskConfig.class); | ||||||
|  |                 UserTask userTask = buildBpmnUserTask(simpleModelNode, userTaskConfig); | ||||||
|                 mainProcess.addFlowElement(userTask); |                 mainProcess.addFlowElement(userTask); | ||||||
|  |                 if (userTaskConfig.getTimeoutHandler() != null && userTaskConfig.getTimeoutHandler().getEnable()) { | ||||||
|  |                     // 添加用户任务的 Timer Boundary Event, 用于任务的超时处理 | ||||||
|  |                     BoundaryEvent boundaryEvent = buildUserTaskTimerBoundaryEvent(userTask, userTaskConfig.getTimeoutHandler()); | ||||||
|  |                     mainProcess.addFlowElement(boundaryEvent); | ||||||
|  |                 } | ||||||
|                 break; |                 break; | ||||||
|             } |             } | ||||||
|             case COPY_TASK: { |             case COPY_TASK: { | ||||||
| @@ -263,6 +270,28 @@ public class SimpleModelUtils { | |||||||
|         } |         } | ||||||
|     } |     } | ||||||
|  |  | ||||||
|  |     private static BoundaryEvent buildUserTaskTimerBoundaryEvent(UserTask userTask, SimpleModelUserTaskConfig.TimeoutHandler timeoutHandler) { | ||||||
|  |         // 定时器边界事件 | ||||||
|  |         BoundaryEvent boundaryEvent = new BoundaryEvent(); | ||||||
|  |         boundaryEvent.setId(IdUtil.fastUUID()); | ||||||
|  |         // 设置关联的任务为不会被中断 | ||||||
|  |         boundaryEvent.setCancelActivity(false); | ||||||
|  |         boundaryEvent.setAttachedToRef(userTask); | ||||||
|  |         TimerEventDefinition eventDefinition = new TimerEventDefinition(); | ||||||
|  |         eventDefinition.setTimeDuration(timeoutHandler.getTimeDuration()); | ||||||
|  |         if (Objects.equals(AUTO_REMINDER.getAction(), timeoutHandler.getAction()) && | ||||||
|  |                 timeoutHandler.getMaxRemindCount() != null && timeoutHandler.getMaxRemindCount() > 1) { | ||||||
|  |             // 最大提醒次数 | ||||||
|  |             eventDefinition.setTimeCycle(String.format("R%d/%s", timeoutHandler.getMaxRemindCount(), timeoutHandler.getTimeDuration())); | ||||||
|  |         } | ||||||
|  |         boundaryEvent.addEventDefinition(eventDefinition); | ||||||
|  |         // 添加定时器边界事件类型 | ||||||
|  |         addExtensionElement(boundaryEvent, TIMER_BOUNDARY_EVENT_TYPE, USER_TASK_TIMEOUT.getType().toString()); | ||||||
|  |         // 添加超时执行动作元素 | ||||||
|  |         addExtensionElement(boundaryEvent, USER_TASK_TIMEOUT_HANDLER_ACTION, StrUtil.toStringOrNull(timeoutHandler.getAction())); | ||||||
|  |         return boundaryEvent; | ||||||
|  |     } | ||||||
|  |  | ||||||
|     private static ParallelGateway buildBpmnParallelGateway(BpmSimpleModelNodeVO node) { |     private static ParallelGateway buildBpmnParallelGateway(BpmSimpleModelNodeVO node) { | ||||||
|         ParallelGateway parallelGateway = new ParallelGateway(); |         ParallelGateway parallelGateway = new ParallelGateway(); | ||||||
|         parallelGateway.setId(node.getId()); |         parallelGateway.setId(node.getId()); | ||||||
| @@ -278,9 +307,14 @@ public class SimpleModelUtils { | |||||||
|         // TODO @jason:建议使用 ServiceTask,通过 executionListeners 实现; |         // TODO @jason:建议使用 ServiceTask,通过 executionListeners 实现; | ||||||
|         // @芋艿 ServiceTask 就可以了吧。 不需要 executionListeners |         // @芋艿 ServiceTask 就可以了吧。 不需要 executionListeners | ||||||
|         // 添加抄送候选人元素 |         // 添加抄送候选人元素 | ||||||
|         addCandidateElements(node, serviceTask); |         addCandidateElements(MapUtil.getInt(node.getAttributes(), BpmnModelConstants.USER_TASK_CANDIDATE_STRATEGY), | ||||||
|  |                 MapUtil.getStr(node.getAttributes(), BpmnModelConstants.USER_TASK_CANDIDATE_PARAM), | ||||||
|  |                 serviceTask); | ||||||
|         // 添加表单字段权限属性元素 |         // 添加表单字段权限属性元素 | ||||||
|         addFormFieldsPermission(node, serviceTask); |         List<Map<String, String>> fieldsPermissions = MapUtil.get(node.getAttributes(), | ||||||
|  |                 FORM_FIELD_PERMISSION_ELEMENT, new TypeReference<>() { | ||||||
|  |                 }); | ||||||
|  |         addFormFieldsPermission(fieldsPermissions, serviceTask); | ||||||
|         return serviceTask; |         return serviceTask; | ||||||
|     } |     } | ||||||
|  |  | ||||||
| @@ -288,12 +322,10 @@ public class SimpleModelUtils { | |||||||
|     /** |     /** | ||||||
|      * 给节点添加候选人元素 |      * 给节点添加候选人元素 | ||||||
|      */ |      */ | ||||||
|     private static void addCandidateElements(BpmSimpleModelNodeVO node, FlowElement flowElement) { |     private static void addCandidateElements(Integer candidateStrategy, String candidateParam, FlowElement flowElement) { | ||||||
|         Integer candidateStrategy = MapUtil.getInt(node.getAttributes(), BpmnModelConstants.USER_TASK_CANDIDATE_STRATEGY); |  | ||||||
|         addExtensionElement(flowElement, BpmnModelConstants.USER_TASK_CANDIDATE_STRATEGY, |         addExtensionElement(flowElement, BpmnModelConstants.USER_TASK_CANDIDATE_STRATEGY, | ||||||
|                 candidateStrategy == null ? null : String.valueOf(candidateStrategy)); |                 candidateStrategy == null ? null : candidateStrategy.toString()); | ||||||
|         addExtensionElement(flowElement, BpmnModelConstants.USER_TASK_CANDIDATE_PARAM, |         addExtensionElement(flowElement, BpmnModelConstants.USER_TASK_CANDIDATE_PARAM, candidateParam); | ||||||
|                 MapUtil.getStr(node.getAttributes(), BpmnModelConstants.USER_TASK_CANDIDATE_PARAM)); |  | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     private static ExclusiveGateway buildBpmnExclusiveGateway(BpmSimpleModelNodeVO node) { |     private static ExclusiveGateway buildBpmnExclusiveGateway(BpmSimpleModelNodeVO node) { | ||||||
| @@ -328,21 +360,25 @@ public class SimpleModelUtils { | |||||||
|         return endEvent; |         return endEvent; | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     private static UserTask buildBpmnUserTask(BpmSimpleModelNodeVO node) { |     private static UserTask buildBpmnUserTask(BpmSimpleModelNodeVO node, SimpleModelUserTaskConfig userTaskConfig) { | ||||||
|         UserTask userTask = new UserTask(); |         UserTask userTask = new UserTask(); | ||||||
|         userTask.setId(node.getId()); |         userTask.setId(node.getId()); | ||||||
|         userTask.setName(node.getName()); |         userTask.setName(node.getName()); | ||||||
|  |         //  设置审批任务的截止时间 | ||||||
|  |         if (userTaskConfig.getTimeoutHandler() != null && userTaskConfig.getTimeoutHandler().getEnable()) { | ||||||
|  |             userTask.setDueDate(userTaskConfig.getTimeoutHandler().getTimeDuration()); | ||||||
|  |         } | ||||||
|  |  | ||||||
|         // 添加候选人元素 |         // 添加候选人元素 | ||||||
|         addCandidateElements(node, userTask); |         addCandidateElements(userTaskConfig.getCandidateStrategy(), userTaskConfig.getCandidateParam(), userTask); | ||||||
|         // 添加表单字段权限属性元素 |         // 添加表单字段权限属性元素 | ||||||
|         addFormFieldsPermission(node, userTask); |         addFormFieldsPermission(userTaskConfig.getFieldsPermission(), userTask); | ||||||
|         // 处理多实例 |         // 处理多实例 | ||||||
|         processMultiInstanceLoopCharacteristics(node, userTask); |         processMultiInstanceLoopCharacteristics(userTaskConfig.getApproveMethod(), userTask); | ||||||
|         return userTask; |         return userTask; | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     private static void processMultiInstanceLoopCharacteristics(BpmSimpleModelNodeVO node, UserTask userTask) { |     private static void processMultiInstanceLoopCharacteristics(Integer approveMethod, UserTask userTask) { | ||||||
|         Integer approveMethod = MapUtil.getInt(node.getAttributes(), SimpleModelConstants.APPROVE_METHOD_ATTRIBUTE); |  | ||||||
|         BpmApproveMethodEnum bpmApproveMethodEnum = BpmApproveMethodEnum.valueOf(approveMethod); |         BpmApproveMethodEnum bpmApproveMethodEnum = BpmApproveMethodEnum.valueOf(approveMethod); | ||||||
|         if (bpmApproveMethodEnum == null || bpmApproveMethodEnum == BpmApproveMethodEnum.SINGLE_PERSON_APPROVE) { |         if (bpmApproveMethodEnum == null || bpmApproveMethodEnum == BpmApproveMethodEnum.SINGLE_PERSON_APPROVE) { | ||||||
|             return; |             return; | ||||||
| @@ -369,10 +405,7 @@ public class SimpleModelUtils { | |||||||
|     /** |     /** | ||||||
|      * 给节点添加表单字段权限元素 |      * 给节点添加表单字段权限元素 | ||||||
|      */ |      */ | ||||||
|     private static void addFormFieldsPermission(BpmSimpleModelNodeVO node, FlowElement flowElement) { |     private static void addFormFieldsPermission(List<Map<String, String>> fieldsPermissions, FlowElement flowElement) { | ||||||
|         List<Map<String, String>> fieldsPermissions = MapUtil.get(node.getAttributes(), |  | ||||||
|                 FORM_FIELD_PERMISSION_ELEMENT, new TypeReference<>() { |  | ||||||
|                 }); |  | ||||||
|         if (CollUtil.isNotEmpty(fieldsPermissions)) { |         if (CollUtil.isNotEmpty(fieldsPermissions)) { | ||||||
|             fieldsPermissions.forEach(item -> addExtensionElement(flowElement, FORM_FIELD_PERMISSION_ELEMENT, item)); |             fieldsPermissions.forEach(item -> addExtensionElement(flowElement, FORM_FIELD_PERMISSION_ELEMENT, item)); | ||||||
|         } |         } | ||||||
|   | |||||||
| @@ -127,6 +127,13 @@ public interface BpmTaskService { | |||||||
|      */ |      */ | ||||||
|     Task getTask(String id); |     Task getTask(String id); | ||||||
|  |  | ||||||
|  |     /** | ||||||
|  |      * 根据条件查询已经分配的用户任务列表 | ||||||
|  |      * @param processInstanceId 流程实例编号 | ||||||
|  |      * @param taskDefineKey 任务定义 Key | ||||||
|  |      */ | ||||||
|  |     List<Task> getAssignedTaskListByConditions(String processInstanceId, String taskDefineKey); | ||||||
|  |  | ||||||
|     /** |     /** | ||||||
|      * 获取当前任务的可回退的 UserTask 集合 |      * 获取当前任务的可回退的 UserTask 集合 | ||||||
|      * |      * | ||||||
|   | |||||||
| @@ -433,6 +433,13 @@ public class BpmTaskServiceImpl implements BpmTaskService { | |||||||
|         return taskService.createTaskQuery().taskId(id).includeTaskLocalVariables().singleResult(); |         return taskService.createTaskQuery().taskId(id).includeTaskLocalVariables().singleResult(); | ||||||
|     } |     } | ||||||
|  |  | ||||||
|  |     @Override | ||||||
|  |     public List<Task> getAssignedTaskListByConditions(String processInstanceId, String defineKey) { | ||||||
|  |         TaskQuery taskQuery = taskService.createTaskQuery().processInstanceId(processInstanceId) | ||||||
|  |                 .taskDefinitionKey(defineKey).active().taskAssigned().includeTaskLocalVariables(); | ||||||
|  |         return taskQuery.list(); | ||||||
|  |     } | ||||||
|  |  | ||||||
|     private HistoricTaskInstance getHistoricTask(String id) { |     private HistoricTaskInstance getHistoricTask(String id) { | ||||||
|         return historyService.createHistoricTaskInstanceQuery().taskId(id).includeTaskLocalVariables().singleResult(); |         return historyService.createHistoricTaskInstanceQuery().taskId(id).includeTaskLocalVariables().singleResult(); | ||||||
|     } |     } | ||||||
|   | |||||||
		Reference in New Issue
	
	Block a user
	 jason
					jason